diff options
author | Ed Maste <emaste@FreeBSD.org> | 2014-02-18 16:23:10 +0000 |
---|---|---|
committer | Ed Maste <emaste@FreeBSD.org> | 2014-02-18 16:23:10 +0000 |
commit | 866dcdacfe59f5f448e008fe2c4cb9dfcf72b2ec (patch) | |
tree | 95cb16075f0af1b3a05b9b84eb18dda8e6c903e9 /source | |
parent | de889deb2c386f2a7831befaf226e5c86685fa53 (diff) | |
download | src-866dcdacfe59f5f448e008fe2c4cb9dfcf72b2ec.tar.gz src-866dcdacfe59f5f448e008fe2c4cb9dfcf72b2ec.zip |
Import lldb as of SVN r201577 (git 2bdc2f6)vendor/lldb/lldb-r201577
(A number of files not required for the FreeBSD build have been removed.)
Sponsored by: DARPA, AFRL
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=262182
svn path=/vendor/lldb/lldb-r201577/; revision=262183; tag=vendor/lldb/lldb-r201577
Diffstat (limited to 'source')
165 files changed, 15549 insertions, 5888 deletions
diff --git a/source/API/SBBreakpoint.cpp b/source/API/SBBreakpoint.cpp index 11ad149fdddc..fbdc0e32f498 100644 --- a/source/API/SBBreakpoint.cpp +++ b/source/API/SBBreakpoint.cpp @@ -23,6 +23,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" diff --git a/source/API/SBCommandInterpreter.cpp b/source/API/SBCommandInterpreter.cpp index ac77e2e41126..f1faa13ba981 100644 --- a/source/API/SBCommandInterpreter.cpp +++ b/source/API/SBCommandInterpreter.cpp @@ -107,6 +107,22 @@ SBCommandInterpreter::AliasExists (const char *cmd) return false; } +bool +SBCommandInterpreter::IsActive () +{ + if (m_opaque_ptr) + return m_opaque_ptr->IsActive (); + return false; +} + +const char * +SBCommandInterpreter::GetIOHandlerControlSequence(char ch) +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetDebugger().GetTopIOHandlerControlSequence (ch).GetCString(); + return NULL; +} + lldb::ReturnStatus SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history) { diff --git a/source/API/SBDebugger.cpp b/source/API/SBDebugger.cpp index 10c0b7dea208..8d6887a6c280 100644 --- a/source/API/SBDebugger.cpp +++ b/source/API/SBDebugger.cpp @@ -20,7 +20,6 @@ #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFrame.h" -#include "lldb/API/SBInputReader.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" @@ -37,6 +36,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Host/DynamicLibrary.h" #include "lldb/Interpreter/Args.h" @@ -49,6 +49,29 @@ using namespace lldb; using namespace lldb_private; +SBInputReader::SBInputReader() +{ +} +SBInputReader::~SBInputReader() +{ +} + +SBError +SBInputReader::Initialize(lldb::SBDebugger& sb_debugger, unsigned long (*)(void*, lldb::SBInputReader*, lldb::InputReaderAction, char const*, unsigned long), void*, lldb::InputReaderGranularity, char const*, char const*, bool) +{ + return SBError(); +} + +void +SBInputReader::SetIsDone(bool) +{ +} +bool +SBInputReader::IsActive() const +{ + return false; +} + static lldb::DynamicLibrarySP LoadPlugin (const lldb::DebuggerSP &debugger_sp, const FileSpec& spec, Error& error) { @@ -111,7 +134,7 @@ SBDebugger::Clear () log->Printf ("SBDebugger(%p)::Clear ()", m_opaque_sp.get()); if (m_opaque_sp) - m_opaque_sp->CleanUpInputReaders (); + m_opaque_sp->ClearIOHandlers (); m_opaque_sp.reset(); } @@ -309,7 +332,11 @@ FILE * SBDebugger::GetInputFileHandle () { if (m_opaque_sp) - return m_opaque_sp->GetInputFile().GetStream(); + { + StreamFileSP stream_file_sp (m_opaque_sp->GetInputFile()); + if (stream_file_sp) + return stream_file_sp->GetFile().GetStream(); + } return NULL; } @@ -317,7 +344,11 @@ FILE * SBDebugger::GetOutputFileHandle () { if (m_opaque_sp) - return m_opaque_sp->GetOutputFile().GetStream(); + { + StreamFileSP stream_file_sp (m_opaque_sp->GetOutputFile()); + if (stream_file_sp) + return stream_file_sp->GetFile().GetStream(); + } return NULL; } @@ -325,7 +356,12 @@ FILE * SBDebugger::GetErrorFileHandle () { if (m_opaque_sp) - return m_opaque_sp->GetErrorFile().GetStream(); + if (m_opaque_sp) + { + StreamFileSP stream_file_sp (m_opaque_sp->GetErrorFile()); + if (stream_file_sp) + return stream_file_sp->GetFile().GetStream(); + } return NULL; } @@ -885,17 +921,17 @@ SBDebugger::DispatchInput (void* baton, const void *data, size_t data_len) void SBDebugger::DispatchInput (const void *data, size_t data_len) { - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::DispatchInput (data=\"%.*s\", size_t=%" PRIu64 ")", - m_opaque_sp.get(), - (int) data_len, - (const char *) data, - (uint64_t)data_len); - - if (m_opaque_sp) - m_opaque_sp->DispatchInput ((const char *) data, data_len); +// Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); +// +// if (log) +// log->Printf ("SBDebugger(%p)::DispatchInput (data=\"%.*s\", size_t=%" PRIu64 ")", +// m_opaque_sp.get(), +// (int) data_len, +// (const char *) data, +// (uint64_t)data_len); +// +// if (m_opaque_sp) +// m_opaque_sp->DispatchInput ((const char *) data, data_len); } void @@ -911,54 +947,18 @@ SBDebugger::DispatchInputEndOfFile () if (m_opaque_sp) m_opaque_sp->DispatchInputEndOfFile (); } - -bool -SBDebugger::InputReaderIsTopReader (const lldb::SBInputReader &reader) -{ - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::InputReaderIsTopReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); - - if (m_opaque_sp && reader.IsValid()) - { - InputReaderSP reader_sp (*reader); - return m_opaque_sp->InputReaderIsTopReader (reader_sp); - } - - return false; -} - void SBDebugger::PushInputReader (SBInputReader &reader) { - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::PushInputReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); - - if (m_opaque_sp && reader.IsValid()) - { - TargetSP target_sp (m_opaque_sp->GetSelectedTarget()); - Mutex::Locker api_locker; - if (target_sp) - api_locker.Lock(target_sp->GetAPIMutex()); - InputReaderSP reader_sp(*reader); - m_opaque_sp->PushInputReader (reader_sp); - } } void -SBDebugger::NotifyTopInputReader (InputReaderAction notification) +SBDebugger::RunCommandInterpreter (bool auto_handle_events, + bool spawn_thread) { - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::NotifyTopInputReader (%d)", m_opaque_sp.get(), notification); - if (m_opaque_sp) - m_opaque_sp->NotifyTopInputReader (notification); + m_opaque_sp->GetCommandInterpreter().RunCommandInterpreter(auto_handle_events, spawn_thread); } void @@ -1050,7 +1050,7 @@ SBDebugger::GetInternalVariableValue (const char *var_name, const char *debugger if (!value_str.empty()) { StringList string_list; - string_list.SplitIntoLines(value_str.c_str(), value_str.size()); + string_list.SplitIntoLines(value_str); return SBStringList(&string_list); } } diff --git a/source/API/SBFrame.cpp b/source/API/SBFrame.cpp index 1a1a63bd0671..cff460208070 100644 --- a/source/API/SBFrame.cpp +++ b/source/API/SBFrame.cpp @@ -1386,20 +1386,22 @@ SBFrame::EvaluateExpression (const char *expr, const SBExpressionOptions &option frame = exe_ctx.GetFramePtr(); if (frame) { -#ifdef LLDB_CONFIGURATION_DEBUG - StreamString frame_description; - frame->DumpUsingSettingsFormat (&frame_description); - Host::SetCrashDescriptionWithFormat ("SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", - expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); -#endif - exe_results = target->EvaluateExpression (expr, + if (target->GetDisplayExpressionsInCrashlogs()) + { + StreamString frame_description; + frame->DumpUsingSettingsFormat (&frame_description); + Host::SetCrashDescriptionWithFormat ("SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", + expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); + } + + exe_results = target->EvaluateExpression (expr, frame, expr_value_sp, options.ref()); expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); -#ifdef LLDB_CONFIGURATION_DEBUG - Host::SetCrashDescription (NULL); -#endif + + if (target->GetDisplayExpressionsInCrashlogs()) + Host::SetCrashDescription (NULL); } else { diff --git a/source/API/SBInputReader.cpp b/source/API/SBInputReader.cpp deleted file mode 100644 index 82b75c869f08..000000000000 --- a/source/API/SBInputReader.cpp +++ /dev/null @@ -1,216 +0,0 @@ -//===-- SBInputReader.cpp ---------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - - -#include "lldb/lldb-enumerations.h" - -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBError.h" -#include "lldb/API/SBInputReader.h" -#include "lldb/API/SBStream.h" -#include "lldb/API/SBStringList.h" -#include "lldb/Core/InputReader.h" -#include "lldb/Core/Log.h" - - -using namespace lldb; -using namespace lldb_private; - -SBInputReader::SBInputReader () : - m_opaque_sp (), - m_callback_function (NULL), - m_callback_baton (NULL) - -{ -} - -SBInputReader::SBInputReader (const lldb::InputReaderSP &reader_sp) : - m_opaque_sp (reader_sp) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBInputReader::SBInputReader (reader_sp=%p) => SBInputReader(%p)", reader_sp.get(), - m_opaque_sp.get()); -} - -SBInputReader::SBInputReader (const SBInputReader &rhs) : - m_opaque_sp (rhs.m_opaque_sp) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf("SBInputReader::SBInputReader (rhs.sp=%p) => SBInputReader(%p)", - rhs.m_opaque_sp.get(), m_opaque_sp.get()); -} - -SBInputReader::~SBInputReader () -{ -} - -size_t -SBInputReader::PrivateCallback -( - void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len -) -{ - SBInputReader *sb_reader = (SBInputReader *)baton; - return sb_reader->m_callback_function (sb_reader->m_callback_baton, - sb_reader, - notification, - bytes, - bytes_len); -} - -SBError -SBInputReader::Initialize -( - SBDebugger &debugger, - Callback callback_function, - void *callback_baton, - lldb::InputReaderGranularity granularity, - const char *end_token, - const char *prompt, - bool echo -) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf("SBInputReader(%p)::Initialize (SBDebugger(%p), callback_function=%p, callback_baton=%p, " - "granularity=%s, end_token=\"%s\", prompt=\"%s\", echo=%i)", - m_opaque_sp.get(), - debugger.get(), - callback_function, - callback_baton, - InputReader::GranularityAsCString (granularity), end_token, prompt, - echo); - - SBError sb_error; - m_opaque_sp.reset (new InputReader (debugger.ref())); - - m_callback_function = callback_function; - m_callback_baton = callback_baton; - - if (m_opaque_sp) - { - sb_error.SetError (m_opaque_sp->Initialize (SBInputReader::PrivateCallback, - this, - granularity, - end_token, - prompt, - echo)); - } - - if (sb_error.Fail()) - { - m_opaque_sp.reset (); - m_callback_function = NULL; - m_callback_baton = NULL; - } - - if (log) - { - SBStream sstr; - sb_error.GetDescription (sstr); - log->Printf ("SBInputReader(%p)::Initialize (...) => SBError(%p): %s", m_opaque_sp.get(), - sb_error.get(), sstr.GetData()); - } - - return sb_error; -} - -bool -SBInputReader::IsValid () const -{ - return (m_opaque_sp.get() != NULL); -} - -const SBInputReader & -SBInputReader::operator = (const SBInputReader &rhs) -{ - if (this != &rhs) - m_opaque_sp = rhs.m_opaque_sp; - return *this; -} - -InputReader * -SBInputReader::operator->() const -{ - return m_opaque_sp.get(); -} - -lldb::InputReaderSP & -SBInputReader::operator *() -{ - return m_opaque_sp; -} - -const lldb::InputReaderSP & -SBInputReader::operator *() const -{ - return m_opaque_sp; -} - -InputReader * -SBInputReader::get() const -{ - return m_opaque_sp.get(); -} - -InputReader & -SBInputReader::ref() const -{ - assert (m_opaque_sp.get()); - return *m_opaque_sp; -} - -bool -SBInputReader::IsDone () const -{ - if (m_opaque_sp) - return m_opaque_sp->IsDone(); - else - return true; -} - -void -SBInputReader::SetIsDone (bool value) -{ - if (m_opaque_sp) - m_opaque_sp->SetIsDone (value); -} - -bool -SBInputReader::IsActive () const -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - bool ret_value = false; - if (m_opaque_sp) - ret_value = m_opaque_sp->IsActive(); - - if (log) - log->Printf ("SBInputReader(%p)::IsActive () => %i", m_opaque_sp.get(), ret_value); - - return ret_value; -} - -InputReaderGranularity -SBInputReader::GetGranularity () -{ - if (m_opaque_sp) - return m_opaque_sp->GetGranularity(); - else - return eInputReaderGranularityInvalid; -} diff --git a/source/API/SBModule.cpp b/source/API/SBModule.cpp index 0285cf304d4d..c8543d4de298 100644 --- a/source/API/SBModule.cpp +++ b/source/API/SBModule.cpp @@ -69,7 +69,7 @@ SBModule::SBModule (lldb::SBProcess &process, lldb::addr_t header_addr) : { Target &target = process_sp->GetTarget(); bool changed = false; - m_opaque_sp->SetLoadAddress(target, 0, changed); + m_opaque_sp->SetLoadAddress(target, 0, true, changed); target.GetImages().Append(m_opaque_sp); } } @@ -579,6 +579,23 @@ SBModule::FindTypes (const char *type) return retval; } +lldb::SBType +SBModule::GetTypeByID (lldb::user_id_t uid) +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + SymbolVendor* vendor = module_sp->GetSymbolVendor(); + if (vendor) + { + Type *type_ptr = vendor->ResolveTypeUID(uid); + if (type_ptr) + return SBType(type_ptr->shared_from_this()); + } + } + return SBType(); +} + lldb::SBTypeList SBModule::GetTypes (uint32_t type_mask) { diff --git a/source/API/SBProcess.cpp b/source/API/SBProcess.cpp index 557006f24345..235388b5f25c 100644 --- a/source/API/SBProcess.cpp +++ b/source/API/SBProcess.cpp @@ -535,6 +535,53 @@ SBProcess::GetThreadAtIndex (size_t index) } uint32_t +SBProcess::GetNumQueues () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num_queues = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + num_queues = process_sp->GetQueueList().GetSize(); + } + + if (log) + log->Printf ("SBProcess(%p)::GetNumQueues () => %d", process_sp.get(), num_queues); + + return num_queues; +} + +SBQueue +SBProcess::GetQueueAtIndex (size_t index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBQueue sb_queue; + QueueSP queue_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index); + sb_queue.SetQueue (queue_sp); + } + + if (log) + { + log->Printf ("SBProcess(%p)::GetQueueAtIndex (index=%d) => SBQueue(%p)", + process_sp.get(), (uint32_t) index, queue_sp.get()); + } + + return sb_queue; +} + + +uint32_t SBProcess::GetStopID(bool include_expression_stops) { ProcessSP process_sp(GetSP()); diff --git a/source/API/SBQueue.cpp b/source/API/SBQueue.cpp new file mode 100644 index 000000000000..8d67a48d6b81 --- /dev/null +++ b/source/API/SBQueue.cpp @@ -0,0 +1,368 @@ +//===-- SBQueue.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBQueue.h" + +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Queue.h" +#include "lldb/Target/QueueItem.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +namespace lldb_private +{ + + class QueueImpl + { + public: + QueueImpl () : + m_queue_wp(), + m_threads(), + m_thread_list_fetched(false), + m_pending_items(), + m_pending_items_fetched(false) + { + } + + QueueImpl (const lldb::QueueSP &queue_sp) : + m_queue_wp(), + m_threads(), + m_thread_list_fetched(false), + m_pending_items(), + m_pending_items_fetched(false) + { + m_queue_wp = queue_sp; + } + + QueueImpl (const QueueImpl &rhs) + { + if (&rhs == this) + return; + m_queue_wp = rhs.m_queue_wp; + m_threads = rhs.m_threads; + m_thread_list_fetched = rhs.m_thread_list_fetched; + m_pending_items = rhs.m_pending_items; + m_pending_items_fetched = rhs.m_pending_items_fetched; + } + + ~QueueImpl () + { + } + + bool + IsValid () + { + return m_queue_wp.lock() != NULL; + } + + void + Clear () + { + m_queue_wp.reset(); + m_thread_list_fetched = false; + m_threads.clear(); + m_pending_items_fetched = false; + m_pending_items.clear(); + } + + void + SetQueue (const lldb::QueueSP &queue_sp) + { + Clear(); + m_queue_wp = queue_sp; + } + + lldb::queue_id_t + GetQueueID () const + { + lldb::queue_id_t result = LLDB_INVALID_QUEUE_ID; + lldb::QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + result = queue_sp->GetID(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBQueue(%p)::GetQueueID () => 0x%" PRIx64, this, result); + return result; + } + + uint32_t + GetIndexID () const + { + uint32_t result = LLDB_INVALID_INDEX32; + lldb::QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + result = queue_sp->GetIndexID(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBQueueImpl(%p)::GetIndexID () => %d", this, result); + return result; + } + + const char * + GetName () const + { + const char *name = NULL; + lldb::QueueSP queue_sp = m_queue_wp.lock (); + if (queue_sp.get()) + { + name = queue_sp->GetName(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBQueueImpl(%p)::GetName () => %s", this, name ? name : "NULL"); + + return name; + } + + void + FetchThreads () + { + if (m_thread_list_fetched == false) + { + lldb::QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock (&queue_sp->GetProcess()->GetRunLock())) + { + const std::vector<ThreadSP> thread_list(queue_sp->GetThreads()); + m_thread_list_fetched = true; + const uint32_t num_threads = thread_list.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + ThreadSP thread_sp = thread_list[idx]; + if (thread_sp && thread_sp->IsValid()) + { + m_threads.push_back (thread_sp); + } + } + } + } + } + } + + void + FetchItems () + { + if (m_pending_items_fetched == false) + { + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock (&queue_sp->GetProcess()->GetRunLock())) + { + const std::vector<QueueItemSP> queue_items(queue_sp->GetPendingItems()); + m_pending_items_fetched = true; + const uint32_t num_pending_items = queue_items.size(); + for (uint32_t idx = 0; idx < num_pending_items; ++idx) + { + QueueItemSP item = queue_items[idx]; + if (item && item->IsValid()) + { + m_pending_items.push_back (item); + } + } + } + } + } + } + + uint32_t + GetNumThreads () + { + uint32_t result = 0; + + FetchThreads(); + if (m_thread_list_fetched) + { + result = m_threads.size(); + } + return result; + } + + lldb::SBThread + GetThreadAtIndex (uint32_t idx) + { + FetchThreads(); + + SBThread sb_thread; + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp && idx < m_threads.size()) + { + ProcessSP process_sp = queue_sp->GetProcess(); + if (process_sp) + { + ThreadSP thread_sp = m_threads[idx].lock(); + if (thread_sp) + { + sb_thread.SetThread (thread_sp); + } + } + } + return sb_thread; + } + + + uint32_t + GetNumPendingItems () + { + uint32_t result = 0; + FetchItems(); + + if (m_pending_items_fetched) + { + result = m_pending_items.size(); + } + return result; + } + + lldb::SBQueueItem + GetPendingItemAtIndex (uint32_t idx) + { + SBQueueItem result; + FetchItems(); + if (m_pending_items_fetched && idx < m_pending_items.size()) + { + result.SetQueueItem (m_pending_items[idx]); + } + return result; + } + + lldb::SBProcess + GetProcess () + { + SBProcess result; + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + result.SetSP (queue_sp->GetProcess()); + } + return result; + } + + private: + lldb::QueueWP m_queue_wp; + std::vector<lldb::ThreadWP> m_threads; // threads currently executing this queue's items + bool m_thread_list_fetched; // have we tried to fetch the threads list already? + std::vector<lldb::QueueItemSP> m_pending_items; // items currently enqueued + bool m_pending_items_fetched; // have we tried to fetch the item list already? + }; + +} + +SBQueue::SBQueue () : + m_opaque_sp (new QueueImpl()) +{ +} + +SBQueue::SBQueue (const QueueSP& queue_sp) : + m_opaque_sp (new QueueImpl (queue_sp)) +{ +} + +SBQueue::SBQueue (const SBQueue &rhs) +{ + if (&rhs == this) + return; + + m_opaque_sp = rhs.m_opaque_sp; +} + +const lldb::SBQueue & +SBQueue::operator = (const lldb::SBQueue &rhs) +{ + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBQueue::~SBQueue() +{ +} + +bool +SBQueue::IsValid() const +{ + return m_opaque_sp->IsValid(); +} + + +void +SBQueue::Clear () +{ + m_opaque_sp->Clear(); +} + + +void +SBQueue::SetQueue (const QueueSP& queue_sp) +{ + m_opaque_sp->SetQueue (queue_sp); +} + +lldb::queue_id_t +SBQueue::GetQueueID () const +{ + return m_opaque_sp->GetQueueID (); +} + +uint32_t +SBQueue::GetIndexID () const +{ + return m_opaque_sp->GetIndexID (); +} + +const char * +SBQueue::GetName () const +{ + return m_opaque_sp->GetName (); +} + +uint32_t +SBQueue::GetNumThreads () +{ + return m_opaque_sp->GetNumThreads (); +} + +SBThread +SBQueue::GetThreadAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetThreadAtIndex (idx); +} + + +uint32_t +SBQueue::GetNumPendingItems () +{ + return m_opaque_sp->GetNumPendingItems (); +} + +SBQueueItem +SBQueue::GetPendingItemAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetPendingItemAtIndex (idx); +} + +SBProcess +SBQueue::GetProcess () +{ + return m_opaque_sp->GetProcess(); +} diff --git a/source/API/SBQueueItem.cpp b/source/API/SBQueueItem.cpp new file mode 100644 index 000000000000..481d51e55426 --- /dev/null +++ b/source/API/SBQueueItem.cpp @@ -0,0 +1,120 @@ +//===-- SBQueueItem.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" +#include "lldb/lldb-forward.h" + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBQueueItem.h" +#include "lldb/API/SBThread.h" +#include "lldb/Core/Address.h" +#include "lldb/Target/QueueItem.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructors +//---------------------------------------------------------------------- +SBQueueItem::SBQueueItem () : + m_queue_item_sp() +{ +} + +SBQueueItem::SBQueueItem (const QueueItemSP& queue_item_sp) : + m_queue_item_sp (queue_item_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBQueueItem::~SBQueueItem() +{ + m_queue_item_sp.reset(); +} + +bool +SBQueueItem::IsValid() const +{ + return m_queue_item_sp.get() != NULL; +} + + +void +SBQueueItem::Clear () +{ + m_queue_item_sp.reset(); +} + + +void +SBQueueItem::SetQueueItem (const QueueItemSP& queue_item_sp) +{ + m_queue_item_sp = queue_item_sp; +} + + +lldb::QueueItemKind +SBQueueItem::GetKind () const +{ + QueueItemKind result = eQueueItemKindUnknown; + if (m_queue_item_sp) + { + result = m_queue_item_sp->GetKind (); + } + return result; +} + +void +SBQueueItem::SetKind (lldb::QueueItemKind kind) +{ + if (m_queue_item_sp) + { + m_queue_item_sp->SetKind (kind); + } +} + +SBAddress +SBQueueItem::GetAddress () const +{ + SBAddress result; + if (m_queue_item_sp) + { + result.SetAddress (&m_queue_item_sp->GetAddress()); + } + return result; +} + +void +SBQueueItem::SetAddress (SBAddress addr) +{ + if (m_queue_item_sp) + { + m_queue_item_sp->SetAddress (addr.ref()); + } +} + +SBThread +SBQueueItem::GetExtendedBacktraceThread (const char *type) +{ + SBThread result; + if (m_queue_item_sp) + { + ThreadSP thread_sp; + ConstString type_const (type); + thread_sp = m_queue_item_sp->GetExtendedBacktraceThread (type_const); + if (thread_sp) + { + result.SetThread (thread_sp); + } + } + return result; +} diff --git a/source/API/SBTarget.cpp b/source/API/SBTarget.cpp index c8bc2171436d..224349c0bce6 100644 --- a/source/API/SBTarget.cpp +++ b/source/API/SBTarget.cpp @@ -52,6 +52,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Process.h" + #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" @@ -688,57 +689,26 @@ SBTarget::Launch return sb_process; } } - else - { - if (listener.IsValid()) - process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); - else - process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); - } - if (process_sp) - { - sb_process.SetSP (process_sp); - if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) - launch_flags |= eLaunchFlagDisableSTDIO; + if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) + launch_flags |= eLaunchFlagDisableSTDIO; - ProcessLaunchInfo launch_info (stdin_path, stdout_path, stderr_path, working_directory, launch_flags); - - Module *exe_module = target_sp->GetExecutableModulePointer(); - if (exe_module) - launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); - if (argv) - launch_info.GetArguments().AppendArguments (argv); - if (envp) - launch_info.GetEnvironmentEntries ().SetArguments (envp); - - error.SetError (process_sp->Launch (launch_info)); - if (error.Success()) - { - // We we are stopping at the entry point, we can return now! - if (stop_at_entry) - return sb_process; - - // Make sure we are stopped at the entry - StateType state = process_sp->WaitForProcessToStop (NULL); - if (state == eStateStopped) - { - // resume the process to skip the entry point - error.SetError (process_sp->Resume()); - if (error.Success()) - { - // If we are doing synchronous mode, then wait for the - // process to stop yet again! - if (target_sp->GetDebugger().GetAsyncExecution () == false) - process_sp->WaitForProcessToStop (NULL); - } - } - } - } + ProcessLaunchInfo launch_info (stdin_path, stdout_path, stderr_path, working_directory, launch_flags); + + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + if (argv) + launch_info.GetArguments().AppendArguments (argv); + if (envp) + launch_info.GetEnvironmentEntries ().SetArguments (envp); + + if (listener.IsValid()) + error.SetError (target_sp->Launch(listener.ref(), launch_info)); else - { - error.SetErrorString ("unable to create lldb_private::Process"); - } + error.SetError (target_sp->Launch(target_sp->GetDebugger().GetListener(), launch_info)); + + sb_process.SetSP(target_sp->GetProcessSP()); } else { @@ -749,7 +719,7 @@ SBTarget::Launch if (log) { log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", - target_sp.get(), process_sp.get()); + target_sp.get(), sb_process.GetSP().get()); } return sb_process; @@ -761,7 +731,6 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBProcess sb_process; - ProcessSP process_sp; TargetSP target_sp(GetSP()); if (log) @@ -773,7 +742,8 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) { Mutex::Locker api_locker (target_sp->GetAPIMutex()); StateType state = eStateInvalid; - process_sp = target_sp->GetProcessSP(); + { + ProcessSP process_sp = target_sp->GetProcessSP(); if (process_sp) { state = process_sp->GetState(); @@ -787,58 +757,20 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) return sb_process; } } - - if (state != eStateConnected) - process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); - - if (process_sp) - { - sb_process.SetSP (process_sp); - lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref(); + } - Module *exe_module = target_sp->GetExecutableModulePointer(); - if (exe_module) - launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref(); - const ArchSpec &arch_spec = target_sp->GetArchitecture(); - if (arch_spec.IsValid()) - launch_info.GetArchitecture () = arch_spec; - - error.SetError (process_sp->Launch (launch_info)); - const bool synchronous_execution = target_sp->GetDebugger().GetAsyncExecution () == false; - if (error.Success()) - { - if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) - { - // If we are doing synchronous mode, then wait for the initial - // stop to happen, else, return and let the caller watch for - // the stop - if (synchronous_execution) - process_sp->WaitForProcessToStop (NULL); - // We we are stopping at the entry point, we can return now! - return sb_process; - } - - // Make sure we are stopped at the entry - StateType state = process_sp->WaitForProcessToStop (NULL); - if (state == eStateStopped) - { - // resume the process to skip the entry point - error.SetError (process_sp->Resume()); - if (error.Success()) - { - // If we are doing synchronous mode, then wait for the - // process to stop yet again! - if (synchronous_execution) - process_sp->WaitForProcessToStop (NULL); - } - } - } - } - else - { - error.SetErrorString ("unable to create lldb_private::Process"); - } + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + + const ArchSpec &arch_spec = target_sp->GetArchitecture(); + if (arch_spec.IsValid()) + launch_info.GetArchitecture () = arch_spec; + + error.SetError (target_sp->Launch (target_sp->GetDebugger().GetListener(), launch_info)); + sb_process.SetSP(target_sp->GetProcessSP()); } else { @@ -848,8 +780,8 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); if (log) { - log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", - target_sp.get(), process_sp.get()); + log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", + target_sp.get(), sb_process.GetSP().get()); } return sb_process; @@ -1263,7 +1195,7 @@ SBTarget::ResolveLoadAddress (lldb::addr_t vm_addr) if (target_sp) { Mutex::Locker api_locker (target_sp->GetAPIMutex()); - if (target_sp->GetSectionLoadList().ResolveLoadAddress (vm_addr, addr)) + if (target_sp->ResolveLoadAddress (vm_addr, addr)) return sb_addr; } @@ -1273,6 +1205,26 @@ SBTarget::ResolveLoadAddress (lldb::addr_t vm_addr) return sb_addr; } + +lldb::SBAddress +SBTarget::ResolvePastLoadAddress (uint32_t stop_id, lldb::addr_t vm_addr) +{ + lldb::SBAddress sb_addr; + Address &addr = sb_addr.ref(); + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (target_sp->ResolveLoadAddress (vm_addr, addr)) + return sb_addr; + } + + // We have a load address that isn't in a section, just return an address + // with the offset filled in (the address) and the section set to NULL + addr.SetRawAddress(vm_addr); + return sb_addr; +} + SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope) @@ -2479,10 +2431,14 @@ SBTarget::SetSectionLoadAddress (lldb::SBSection section, } else { - if (target_sp->GetSectionLoadList().SetSectionLoadAddress (section_sp, section_base_addr)) + ProcessSP process_sp (target_sp->GetProcessSP()); + uint32_t stop_id = 0; + if (process_sp) + stop_id = process_sp->GetStopID(); + + if (target_sp->SetSectionLoadAddress (section_sp, section_base_addr)) { // Flush info in the process (stack frames, etc) - ProcessSP process_sp (target_sp->GetProcessSP()); if (process_sp) process_sp->Flush(); } @@ -2511,10 +2467,14 @@ SBTarget::ClearSectionLoadAddress (lldb::SBSection section) } else { - if (target_sp->GetSectionLoadList().SetSectionUnloaded (section.GetSP())) + ProcessSP process_sp (target_sp->GetProcessSP()); + uint32_t stop_id = 0; + if (process_sp) + stop_id = process_sp->GetStopID(); + + if (target_sp->SetSectionUnloaded (section.GetSP())) { // Flush info in the process (stack frames, etc) - ProcessSP process_sp (target_sp->GetProcessSP()); if (process_sp) process_sp->Flush(); } @@ -2539,7 +2499,7 @@ SBTarget::SetModuleLoadAddress (lldb::SBModule module, int64_t slide_offset) if (module_sp) { bool changed = false; - if (module_sp->SetLoadAddress (*target_sp, slide_offset, changed)) + if (module_sp->SetLoadAddress (*target_sp, slide_offset, true, changed)) { // The load was successful, make sure that at least some sections // changed before we notify that our module was loaded. @@ -2586,13 +2546,18 @@ SBTarget::ClearModuleLoadAddress (lldb::SBModule module) SectionList *section_list = objfile->GetSectionList(); if (section_list) { + ProcessSP process_sp (target_sp->GetProcessSP()); + uint32_t stop_id = 0; + if (process_sp) + stop_id = process_sp->GetStopID(); + bool changed = false; const size_t num_sections = section_list->GetSize(); for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { SectionSP section_sp (section_list->GetSectionAtIndex(sect_idx)); if (section_sp) - changed |= target_sp->GetSectionLoadList().SetSectionUnloaded (section_sp) > 0; + changed |= target_sp->SetSectionUnloaded (section_sp) > 0; } if (changed) { diff --git a/source/API/SBType.cpp b/source/API/SBType.cpp index 3055c2752083..5ca7ddf3d813 100644 --- a/source/API/SBType.cpp +++ b/source/API/SBType.cpp @@ -186,6 +186,14 @@ SBType::GetReferenceType() } SBType +SBType::GetTypedefedType() +{ + if (!IsValid()) + return SBType(); + return SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetTypedefedType()))); +} + +SBType SBType::GetDereferencedType() { if (!IsValid()) diff --git a/source/API/SBTypeCategory.cpp b/source/API/SBTypeCategory.cpp index 08fdefad1be8..9fe4dad01a9f 100644 --- a/source/API/SBTypeCategory.cpp +++ b/source/API/SBTypeCategory.cpp @@ -87,7 +87,7 @@ SBTypeCategory::GetNumFormats () if (!IsValid()) return 0; - return m_opaque_sp->GetValueNavigator()->GetCount() + m_opaque_sp->GetRegexValueNavigator()->GetCount(); + return m_opaque_sp->GetTypeFormatsContainer()->GetCount() + m_opaque_sp->GetRegexTypeFormatsContainer()->GetCount(); } uint32_t @@ -95,7 +95,7 @@ SBTypeCategory::GetNumSummaries () { if (!IsValid()) return 0; - return m_opaque_sp->GetSummaryNavigator()->GetCount() + m_opaque_sp->GetRegexSummaryNavigator()->GetCount(); + return m_opaque_sp->GetTypeSummariesContainer()->GetCount() + m_opaque_sp->GetRegexTypeSummariesContainer()->GetCount(); } uint32_t @@ -103,7 +103,7 @@ SBTypeCategory::GetNumFilters () { if (!IsValid()) return 0; - return m_opaque_sp->GetFilterNavigator()->GetCount() + m_opaque_sp->GetRegexFilterNavigator()->GetCount(); + return m_opaque_sp->GetTypeFiltersContainer()->GetCount() + m_opaque_sp->GetRegexTypeFiltersContainer()->GetCount(); } #ifndef LLDB_DISABLE_PYTHON @@ -112,7 +112,7 @@ SBTypeCategory::GetNumSynthetics () { if (!IsValid()) return 0; - return m_opaque_sp->GetSyntheticNavigator()->GetCount() + m_opaque_sp->GetRegexSyntheticNavigator()->GetCount(); + return m_opaque_sp->GetTypeSyntheticsContainer()->GetCount() + m_opaque_sp->GetRegexTypeSyntheticsContainer()->GetCount(); } #endif @@ -162,9 +162,9 @@ SBTypeCategory::GetFilterForType (SBTypeNameSpecifier spec) lldb::SyntheticChildrenSP children_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetRegexTypeFiltersContainer()->GetExact(ConstString(spec.GetName()), children_sp); else - m_opaque_sp->GetFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetTypeFiltersContainer()->GetExact(ConstString(spec.GetName()), children_sp); if (!children_sp) return lldb::SBTypeFilter(); @@ -186,9 +186,9 @@ SBTypeCategory::GetFormatForType (SBTypeNameSpecifier spec) lldb::TypeFormatImplSP format_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexValueNavigator()->GetExact(ConstString(spec.GetName()), format_sp); + m_opaque_sp->GetRegexTypeFormatsContainer()->GetExact(ConstString(spec.GetName()), format_sp); else - m_opaque_sp->GetValueNavigator()->GetExact(ConstString(spec.GetName()), format_sp); + m_opaque_sp->GetTypeFormatsContainer()->GetExact(ConstString(spec.GetName()), format_sp); if (!format_sp) return lldb::SBTypeFormat(); @@ -209,9 +209,9 @@ SBTypeCategory::GetSummaryForType (SBTypeNameSpecifier spec) lldb::TypeSummaryImplSP summary_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + m_opaque_sp->GetRegexTypeSummariesContainer()->GetExact(ConstString(spec.GetName()), summary_sp); else - m_opaque_sp->GetSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + m_opaque_sp->GetTypeSummariesContainer()->GetExact(ConstString(spec.GetName()), summary_sp); if (!summary_sp) return lldb::SBTypeSummary(); @@ -233,9 +233,9 @@ SBTypeCategory::GetSyntheticForType (SBTypeNameSpecifier spec) lldb::SyntheticChildrenSP children_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetRegexTypeSyntheticsContainer()->GetExact(ConstString(spec.GetName()), children_sp); else - m_opaque_sp->GetSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetTypeSyntheticsContainer()->GetExact(ConstString(spec.GetName()), children_sp); if (!children_sp) return lldb::SBTypeSynthetic(); @@ -312,9 +312,9 @@ SBTypeCategory::AddTypeFormat (SBTypeNameSpecifier type_name, return false; if (type_name.IsRegex()) - m_opaque_sp->GetRegexValueNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), format.GetSP()); + m_opaque_sp->GetRegexTypeFormatsContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), format.GetSP()); else - m_opaque_sp->GetValueNavigator()->Add(ConstString(type_name.GetName()), format.GetSP()); + m_opaque_sp->GetTypeFormatsContainer()->Add(ConstString(type_name.GetName()), format.GetSP()); return true; } @@ -329,9 +329,9 @@ SBTypeCategory::DeleteTypeFormat (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexValueNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeFormatsContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetValueNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeFormatsContainer()->Delete(ConstString(type_name.GetName())); } #ifndef LLDB_DISABLE_PYTHON @@ -383,9 +383,9 @@ SBTypeCategory::AddTypeSummary (SBTypeNameSpecifier type_name, } if (type_name.IsRegex()) - m_opaque_sp->GetRegexSummaryNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), summary.GetSP()); + m_opaque_sp->GetRegexTypeSummariesContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), summary.GetSP()); else - m_opaque_sp->GetSummaryNavigator()->Add(ConstString(type_name.GetName()), summary.GetSP()); + m_opaque_sp->GetTypeSummariesContainer()->Add(ConstString(type_name.GetName()), summary.GetSP()); return true; } @@ -401,9 +401,9 @@ SBTypeCategory::DeleteTypeSummary (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexSummaryNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeSummariesContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetSummaryNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeSummariesContainer()->Delete(ConstString(type_name.GetName())); } bool @@ -420,9 +420,9 @@ SBTypeCategory::AddTypeFilter (SBTypeNameSpecifier type_name, return false; if (type_name.IsRegex()) - m_opaque_sp->GetRegexFilterNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), filter.GetSP()); + m_opaque_sp->GetRegexTypeFiltersContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), filter.GetSP()); else - m_opaque_sp->GetFilterNavigator()->Add(ConstString(type_name.GetName()), filter.GetSP()); + m_opaque_sp->GetTypeFiltersContainer()->Add(ConstString(type_name.GetName()), filter.GetSP()); return true; } @@ -437,9 +437,9 @@ SBTypeCategory::DeleteTypeFilter (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexFilterNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeFiltersContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetFilterNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeFiltersContainer()->Delete(ConstString(type_name.GetName())); } #ifndef LLDB_DISABLE_PYTHON @@ -491,9 +491,9 @@ SBTypeCategory::AddTypeSynthetic (SBTypeNameSpecifier type_name, } if (type_name.IsRegex()) - m_opaque_sp->GetRegexSyntheticNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), synth.GetSP()); + m_opaque_sp->GetRegexTypeSyntheticsContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), synth.GetSP()); else - m_opaque_sp->GetSyntheticNavigator()->Add(ConstString(type_name.GetName()), synth.GetSP()); + m_opaque_sp->GetTypeSyntheticsContainer()->Add(ConstString(type_name.GetName()), synth.GetSP()); return true; } @@ -508,9 +508,9 @@ SBTypeCategory::DeleteTypeSynthetic (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexSyntheticNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeSyntheticsContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetSyntheticNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeSyntheticsContainer()->Delete(ConstString(type_name.GetName())); } #endif // LLDB_DISABLE_PYTHON diff --git a/source/API/SBTypeFormat.cpp b/source/API/SBTypeFormat.cpp index 34ab404a206a..d3ec9bc00bd0 100644 --- a/source/API/SBTypeFormat.cpp +++ b/source/API/SBTypeFormat.cpp @@ -25,7 +25,13 @@ m_opaque_sp() SBTypeFormat::SBTypeFormat (lldb::Format format, uint32_t options) -: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl(format,options))) +: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_Format(format,options))) +{ +} + +SBTypeFormat::SBTypeFormat (const char* type, + uint32_t options) +: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_EnumType(ConstString(type ? type : ""),options))) { } @@ -47,11 +53,19 @@ SBTypeFormat::IsValid() const lldb::Format SBTypeFormat::GetFormat () { - if (IsValid()) - return m_opaque_sp->GetFormat(); + if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) + return ((TypeFormatImpl_Format*)m_opaque_sp.get())->GetFormat(); return lldb::eFormatInvalid; } +const char* +SBTypeFormat::GetTypeName () +{ + if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum) + return ((TypeFormatImpl_EnumType*)m_opaque_sp.get())->GetTypeName().AsCString(""); + return ""; +} + uint32_t SBTypeFormat::GetOptions() { @@ -63,14 +77,21 @@ SBTypeFormat::GetOptions() void SBTypeFormat::SetFormat (lldb::Format fmt) { - if (CopyOnWrite_Impl()) - m_opaque_sp->SetFormat(fmt); + if (CopyOnWrite_Impl(Type::eTypeFormat)) + ((TypeFormatImpl_Format*)m_opaque_sp.get())->SetFormat(fmt); +} + +void +SBTypeFormat::SetTypeName (const char* type) +{ + if (CopyOnWrite_Impl(Type::eTypeEnum)) + ((TypeFormatImpl_EnumType*)m_opaque_sp.get())->SetTypeName(ConstString(type ? type : "")); } void SBTypeFormat::SetOptions (uint32_t value) { - if (CopyOnWrite_Impl()) + if (CopyOnWrite_Impl(Type::eTypeKeepSame)) m_opaque_sp->SetOptions(value); } @@ -143,13 +164,30 @@ SBTypeFormat::SBTypeFormat (const lldb::TypeFormatImplSP &typeformat_impl_sp) : } bool -SBTypeFormat::CopyOnWrite_Impl() +SBTypeFormat::CopyOnWrite_Impl(Type type) { if (!IsValid()) return false; - if (m_opaque_sp.unique()) + + if (m_opaque_sp.unique() && + ((type == Type::eTypeKeepSame) || + (type == Type::eTypeFormat && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) || + (type == Type::eTypeEnum && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum)) + ) return true; - SetSP(TypeFormatImplSP(new TypeFormatImpl(GetFormat(),GetOptions()))); + if (type == Type::eTypeKeepSame) + { + if (m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) + type = Type::eTypeFormat; + else + type = Type::eTypeEnum; + } + + if (type == Type::eTypeFormat) + SetSP(TypeFormatImplSP(new TypeFormatImpl_Format(GetFormat(),GetOptions()))); + else + SetSP(TypeFormatImplSP(new TypeFormatImpl_EnumType(ConstString(GetTypeName()),GetOptions()))); + return true; } diff --git a/source/API/SBValue.cpp b/source/API/SBValue.cpp index 51b6790dd2b8..4bd018352ff2 100644 --- a/source/API/SBValue.cpp +++ b/source/API/SBValue.cpp @@ -96,7 +96,24 @@ public: bool IsValid () { - return m_valobj_sp.get() != NULL; + if (m_valobj_sp.get() == NULL) + return false; + else + { + // FIXME: This check is necessary but not sufficient. We for sure don't want to touch SBValues whose owning + // targets have gone away. This check is a little weak in that it enforces that restriction when you call + // IsValid, but since IsValid doesn't lock the target, you have no guarantee that the SBValue won't go + // invalid after you call this... + // Also, an SBValue could depend on data from one of the modules in the target, and those could go away + // independently of the target, for instance if a module is unloaded. But right now, neither SBValues + // nor ValueObjects know which modules they depend on. So I have no good way to make that check without + // tracking that in all the ValueObject subclasses. + TargetSP target_sp = m_valobj_sp->GetTargetSP(); + if (target_sp && target_sp->IsValid()) + return true; + else + return false; + } } lldb::ValueObjectSP @@ -120,6 +137,8 @@ public: Target *target = value_sp->GetTargetSP().get(); if (target) api_locker.Lock(target->GetAPIMutex()); + else + return ValueObjectSP(); ProcessSP process_sp(value_sp->GetProcessSP()); if (process_sp && !stop_locker.TryLock (&process_sp->GetRunLock())) @@ -276,7 +295,7 @@ SBValue::IsValid () // If this function ever changes to anything that does more than just // check if the opaque shared pointer is non NULL, then we need to update // all "if (m_opaque_sp)" code in this file. - return m_opaque_sp.get() != NULL && m_opaque_sp->GetRootSP().get() != NULL; + return m_opaque_sp.get() != NULL && m_opaque_sp->IsValid() && m_opaque_sp->GetRootSP().get() != NULL; } void diff --git a/source/Breakpoint/Breakpoint.cpp b/source/Breakpoint/Breakpoint.cpp index 32c0b1066f8e..5ce064fc41a0 100644 --- a/source/Breakpoint/Breakpoint.cpp +++ b/source/Breakpoint/Breakpoint.cpp @@ -45,14 +45,19 @@ Breakpoint::GetEventIdentifier () //---------------------------------------------------------------------- // Breakpoint constructor //---------------------------------------------------------------------- -Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool hardware) : +Breakpoint::Breakpoint(Target &target, + SearchFilterSP &filter_sp, + BreakpointResolverSP &resolver_sp, + bool hardware, + bool resolve_indirect_symbols) : m_being_created(true), m_hardware(hardware), m_target (target), m_filter_sp (filter_sp), m_resolver_sp (resolver_sp), m_options (), - m_locations (*this) + m_locations (*this), + m_resolve_indirect_symbols(resolve_indirect_symbols) { m_being_created = false; } @@ -87,7 +92,7 @@ Breakpoint::GetTarget () const BreakpointLocationSP Breakpoint::AddLocation (const Address &addr, bool *new_location) { - return m_locations.AddLocation (addr, new_location); + return m_locations.AddLocation (addr, m_resolve_indirect_symbols, new_location); } BreakpointLocationSP diff --git a/source/Breakpoint/BreakpointLocation.cpp b/source/Breakpoint/BreakpointLocation.cpp index 5009e862d84b..2c75a11e9788 100644 --- a/source/Breakpoint/BreakpointLocation.cpp +++ b/source/Breakpoint/BreakpointLocation.cpp @@ -39,16 +39,29 @@ BreakpointLocation::BreakpointLocation Breakpoint &owner, const Address &addr, lldb::tid_t tid, - bool hardware + bool hardware, + bool check_for_resolver ) : StoppointLocation (loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), hardware), m_being_created(true), + m_should_resolve_indirect_functions (false), + m_is_reexported (false), + m_is_indirect (false), m_address (addr), m_owner (owner), m_options_ap (), m_bp_site_sp (), m_condition_mutex () { + if (check_for_resolver) + { + Symbol *symbol = m_address.CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) + { + SetShouldResolveIndirectFunctions (true); + } + } + SetThreadID (tid); m_being_created = false; } @@ -545,7 +558,10 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) if (level == lldb::eDescriptionLevelFull || level == eDescriptionLevelInitial) { - s->PutCString("where = "); + if (IsReExported()) + s->PutCString ("re-exported target = "); + else + s->PutCString("where = "); sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); } else @@ -584,7 +600,10 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) if (sc.symbol) { s->EOL(); - s->Indent("symbol = "); + if (IsReExported()) + s->Indent ("re-exported target = "); + else + s->Indent("symbol = "); s->PutCString(sc.symbol->GetMangled().GetName().AsCString("<unknown>")); } } @@ -612,6 +631,24 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); else m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); + + if (IsIndirect() && m_bp_site_sp) + { + Address resolved_address; + resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target); + Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol(); + if (resolved_symbol) + { + if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial) + s->Printf (", "); + else if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL(); + s->Indent(); + } + s->Printf ("indirect target = %s", resolved_symbol->GetName().GetCString()); + } + } if (level == lldb::eDescriptionLevelVerbose) { diff --git a/source/Breakpoint/BreakpointLocationList.cpp b/source/Breakpoint/BreakpointLocationList.cpp index 18147deca3ec..917c776e75d2 100644 --- a/source/Breakpoint/BreakpointLocationList.cpp +++ b/source/Breakpoint/BreakpointLocationList.cpp @@ -19,8 +19,10 @@ #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" + using namespace lldb; using namespace lldb_private; @@ -39,12 +41,12 @@ BreakpointLocationList::~BreakpointLocationList() } BreakpointLocationSP -BreakpointLocationList::Create (const Address &addr) +BreakpointLocationList::Create (const Address &addr, bool resolve_indirect_symbols) { Mutex::Locker locker (m_mutex); // The location ID is just the size of the location list + 1 lldb::break_id_t bp_loc_id = ++m_next_id; - BreakpointLocationSP bp_loc_sp (new BreakpointLocation (bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID, m_owner.IsHardware())); + BreakpointLocationSP bp_loc_sp (new BreakpointLocation (bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID, m_owner.IsHardware(), resolve_indirect_symbols)); m_locations.push_back (bp_loc_sp); m_address_to_location[addr] = bp_loc_sp; return bp_loc_sp; @@ -245,7 +247,7 @@ BreakpointLocationList::GetDescription (Stream *s, lldb::DescriptionLevel level) } BreakpointLocationSP -BreakpointLocationList::AddLocation (const Address &addr, bool *new_location) +BreakpointLocationList::AddLocation (const Address &addr, bool resolve_indirect_symbols, bool *new_location) { Mutex::Locker locker (m_mutex); @@ -254,7 +256,7 @@ BreakpointLocationList::AddLocation (const Address &addr, bool *new_location) BreakpointLocationSP bp_loc_sp (FindByAddress(addr)); if (!bp_loc_sp) { - bp_loc_sp = Create (addr); + bp_loc_sp = Create (addr, resolve_indirect_symbols); if (bp_loc_sp) { bp_loc_sp->ResolveBreakpointSite(); diff --git a/source/Breakpoint/BreakpointResolverName.cpp b/source/Breakpoint/BreakpointResolverName.cpp index c82dd5ee050b..cf5d89cb7a8b 100644 --- a/source/Breakpoint/BreakpointResolverName.cpp +++ b/source/Breakpoint/BreakpointResolverName.cpp @@ -272,6 +272,8 @@ BreakpointResolverName::SearchCallback { if (func_list.GetContextAtIndex(i, sc)) { + bool is_reexported = false; + if (sc.block && sc.block->GetInlinedFunctionInfo()) { if (!sc.block->GetStartAddress(break_addr)) @@ -293,7 +295,10 @@ BreakpointResolverName::SearchCallback { const Symbol *actual_symbol = sc.symbol->ResolveReExportedSymbol(m_breakpoint->GetTarget()); if (actual_symbol) + { + is_reexported = true; break_addr = actual_symbol->GetAddress(); + } } else { @@ -313,6 +318,7 @@ BreakpointResolverName::SearchCallback if (filter.AddressPasses(break_addr)) { BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(break_addr, &new_location)); + bp_loc_sp->SetIsReExported(is_reexported); if (bp_loc_sp && new_location && !m_breakpoint->IsInternal()) { if (log) diff --git a/source/Commands/CommandObjectBreakpointCommand.cpp b/source/Commands/CommandObjectBreakpointCommand.cpp index e540461dadae..532d6cedc83e 100644 --- a/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/source/Commands/CommandObjectBreakpointCommand.cpp @@ -16,6 +16,7 @@ #include "CommandObjectBreakpointCommand.h" #include "CommandObjectBreakpoint.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" @@ -34,7 +35,9 @@ using namespace lldb_private; //------------------------------------------------------------------------- -class CommandObjectBreakpointCommandAdd : public CommandObjectParsed +class CommandObjectBreakpointCommandAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: @@ -43,6 +46,7 @@ public: "add", "Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit.", NULL), + IOHandlerDelegateMultiline ("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong ( @@ -207,42 +211,47 @@ one command per line.\n" ); return &m_options; } - void - CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, - CommandReturnObject &result) + virtual void + IOHandlerActivated (IOHandler &io_handler) { - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - std::unique_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData()); - if (reader_sp && data_ap.get()) + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); - bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); - - Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback, - bp_options, // baton - eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token - "> ", // prompt - true)); // echo input - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } + output_sp->PutCString(g_reader_instructions); + output_sp->Flush(); } - else + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &line) + { + io_handler.SetIsDone(true); + + BreakpointOptions *bp_options = (BreakpointOptions *) io_handler.GetUserData(); + if (bp_options) { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); + std::unique_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData()); + if (data_ap.get()) + { + data_ap->user_source.SplitIntoLines (line.c_str(), line.size()); + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); + } } } + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) + { + m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + bp_options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + } + /// Set a one-liner as the callback for the breakpoint. void SetBreakpointCommandCallback (BreakpointOptions *bp_options, @@ -262,93 +271,6 @@ one command per line.\n" ); return; } - - static size_t - GenerateBreakpointCommandCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_reader_instructions); - if (reader.GetPrompt()) - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderReactivate: - if (reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - if (bytes && bytes_len && baton) - { - BreakpointOptions *bp_options = (BreakpointOptions *) baton; - if (bp_options) - { - Baton *bp_options_baton = bp_options->GetBaton(); - if (bp_options_baton) - ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); - } - } - if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderInterrupt: - { - // Finish, and cancel the breakpoint command. - reader.SetIsDone (true); - BreakpointOptions *bp_options = (BreakpointOptions *) baton; - if (bp_options) - { - Baton *bp_options_baton = bp_options->GetBaton (); - if (bp_options_baton) - { - ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->user_source.Clear(); - ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->script_source.clear(); - } - } - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to breakpoint.\n"); - out_stream->Flush(); - } - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - break; - } - - return bytes_len; - } static bool BreakpointOptionsCallbackFunction (void *baton, @@ -623,7 +545,7 @@ private: }; const char * -CommandObjectBreakpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; +CommandObjectBreakpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end.\n"; // FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting // language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper. diff --git a/source/Commands/CommandObjectBreakpointCommand.h b/source/Commands/CommandObjectBreakpointCommand.h index afedb7602cdd..e91790779510 100644 --- a/source/Commands/CommandObjectBreakpointCommand.h +++ b/source/Commands/CommandObjectBreakpointCommand.h @@ -19,7 +19,6 @@ #include "lldb/lldb-types.h" #include "lldb/Interpreter/Options.h" -#include "lldb/Core/InputReader.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" diff --git a/source/Commands/CommandObjectCommands.cpp b/source/Commands/CommandObjectCommands.cpp index 6824ead0d9e0..7bfdec094d6c 100644 --- a/source/Commands/CommandObjectCommands.cpp +++ b/source/Commands/CommandObjectCommands.cpp @@ -18,8 +18,7 @@ // Project includes #include "lldb/Core/Debugger.h" -#include "lldb/Core/InputReader.h" -#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/StringList.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandHistory.h" @@ -309,7 +308,9 @@ protected: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter), - m_stop_on_error (true) + m_stop_on_error (true), + m_silent_run (false), + m_stop_on_continue (true) { } @@ -321,23 +322,21 @@ protected: { Error error; const int short_option = m_getopt_table[option_idx].val; - bool success; switch (short_option) { case 'e': error = m_stop_on_error.SetValueFromCString(option_arg); break; + case 'c': - m_stop_on_continue = Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("invalid value for stop-on-continue: %s", option_arg); + error = m_stop_on_continue.SetValueFromCString(option_arg); break; + case 's': - m_silent_run = Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("invalid value for silent-run: %s", option_arg); + error = m_silent_run.SetValueFromCString(option_arg); break; + default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; @@ -350,8 +349,8 @@ protected: OptionParsingStarting () { m_stop_on_error.Clear(); - m_silent_run = false; - m_stop_on_continue = true; + m_silent_run.Clear(); + m_stop_on_continue.Clear(); } const OptionDefinition* @@ -367,8 +366,8 @@ protected: // Instance variables to hold the values for command options. OptionValueBoolean m_stop_on_error; - bool m_silent_run; - bool m_stop_on_continue; + OptionValueBoolean m_silent_run; + OptionValueBoolean m_stop_on_continue; }; bool @@ -379,22 +378,40 @@ protected: { const char *filename = command.GetArgumentAtIndex(0); - result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename); - FileSpec cmd_file (filename, true); ExecutionContext *exe_ctx = NULL; // Just use the default context. - bool echo_commands = !m_options.m_silent_run; - bool print_results = true; - bool stop_on_error = m_options.m_stop_on_error.OptionWasSet() ? (bool)m_options.m_stop_on_error : m_interpreter.GetStopCmdSourceOnError(); - - m_interpreter.HandleCommandsFromFile (cmd_file, - exe_ctx, - m_options.m_stop_on_continue, - stop_on_error, - echo_commands, - print_results, - eLazyBoolCalculate, - result); + + // If any options were set, then use them + if (m_options.m_stop_on_error.OptionWasSet() || + m_options.m_silent_run.OptionWasSet() || + m_options.m_stop_on_continue.OptionWasSet()) + { + // Use user set settings + LazyBool print_command = m_options.m_silent_run.GetCurrentValue() ? eLazyBoolNo : eLazyBoolYes; + m_interpreter.HandleCommandsFromFile (cmd_file, + exe_ctx, + m_options.m_stop_on_continue.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on continue + m_options.m_stop_on_error.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on error + print_command, // Echo command + print_command, // Print command output + eLazyBoolCalculate, // Add to history + result); + + } + else + { + // No options were set, inherit any settings from nested "command source" commands, + // or set to sane default settings... + m_interpreter.HandleCommandsFromFile (cmd_file, + exe_ctx, + eLazyBoolCalculate, // Stop on continue + eLazyBoolCalculate, // Stop on error + eLazyBoolCalculate, // Echo command + eLazyBoolCalculate, // Print command output + eLazyBoolCalculate, // Add to history + result); + + } } else { @@ -423,7 +440,7 @@ CommandObjectCommandsSource::CommandOptions::g_option_table[] = static const char *g_python_command_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" "You must define a Python function with this signature:\n" - "def my_command_impl(debugger, args, result, internal_dict):"; + "def my_command_impl(debugger, args, result, internal_dict):\n"; class CommandObjectCommandsAlias : public CommandObjectRaw @@ -856,7 +873,9 @@ protected: //------------------------------------------------------------------------- #pragma mark CommandObjectCommandsAddRegex -class CommandObjectCommandsAddRegex : public CommandObjectParsed +class CommandObjectCommandsAddRegex : + public CommandObjectParsed, + public IOHandlerDelegate { public: CommandObjectCommandsAddRegex (CommandInterpreter &interpreter) : @@ -864,6 +883,7 @@ public: "command regex", "Allow the user to create a regular expression command.", "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), + IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong( @@ -899,6 +919,97 @@ public: protected: + + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) + { + output_sp->PutCString("Enter one of more sed substitution commands in the form: 's/<regex>/<subst>/'.\nTerminate the substitution list with an empty line.\n"); + output_sp->Flush(); + } + } + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + io_handler.SetIsDone(true); + if (m_regex_cmd_ap.get()) + { + StringList lines; + if (lines.SplitIntoLines (data)) + { + const size_t num_lines = lines.GetSize(); + bool check_only = false; + for (size_t i=0; i<num_lines; ++i) + { + printf ("regex[%zu] = %s\n", i, lines[i].c_str()); + llvm::StringRef bytes_strref (lines[i]); + Error error = AppendRegexSubstitution (bytes_strref, check_only); + if (error.Fail()) + { + if (!m_interpreter.GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) + { + StreamSP out_stream = m_interpreter.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf("error: %s\n", error.AsCString()); + } + } + } + } + if (m_regex_cmd_ap->HasRegexEntries()) + { + CommandObjectSP cmd_sp (m_regex_cmd_ap.release()); + m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); + } + } + } + + virtual LineStatus + IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error) + { + if (line_idx == UINT32_MAX) + { + // Return true to indicate we are done getting lines (this + // is a "fake" line - the real terminating blank line was + // removed during a previous call with the code below) + error.Clear(); + return LineStatus::Done; + } + else + { + const size_t num_lines = lines.GetSize(); + if (line_idx + 1 == num_lines) + { + // The last line was edited, if this line is empty, then we are done + // getting our multiple lines. + if (lines[line_idx].empty()) + { + // Remove the last empty line from "lines" so it doesn't appear + // in our final expression and return true to indicate we are done + // getting lines + lines.PopBack(); + return LineStatus::Done; + } + } + // Check the current line to make sure it is formatted correctly + bool check_only = true; + llvm::StringRef regex_sed(lines[line_idx]); + error = AppendRegexSubstitution (regex_sed, check_only); + if (error.Fail()) + { + return LineStatus::Error; + } + else + { + return LineStatus::Success; + } + } + } + bool DoExecute (Args& command, CommandReturnObject &result) { @@ -920,21 +1031,18 @@ protected: if (argc == 1) { - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - if (reader_sp) + Debugger &debugger = m_interpreter.GetDebugger(); + const bool multiple_lines = true; // Get multiple lines + IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + "lldb", // Name of input reader for history + "\033[K> ", // Prompt and clear line + multiple_lines, + *this)); + + if (io_handler_sp) { - error =reader_sp->Initialize (CommandObjectCommandsAddRegex::InputReaderCallback, - this, // baton - eInputReaderGranularityLine, // token size, to pass to callback function - NULL, // end token - "> ", // prompt - true); // echo input - if (error.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - return true; - } + debugger.PushIOHandler(io_handler_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); } } else @@ -942,7 +1050,8 @@ protected: for (size_t arg_idx = 1; arg_idx < argc; ++arg_idx) { llvm::StringRef arg_strref (command.GetArgumentAtIndex(arg_idx)); - error = AppendRegexSubstitution (arg_strref); + bool check_only = false; + error = AppendRegexSubstitution (arg_strref, check_only); if (error.Fail()) break; } @@ -963,7 +1072,7 @@ protected: } Error - AppendRegexSubstitution (const llvm::StringRef ®ex_sed) + AppendRegexSubstitution (const llvm::StringRef ®ex_sed, bool check_only) { Error error; @@ -1053,10 +1162,14 @@ protected: regex_sed.data()); return error; } - std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); - std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); - m_regex_cmd_ap->AddRegexCommand (regex.c_str(), - subst.c_str()); + + if (check_only == false) + { + std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); + std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); + m_regex_cmd_ap->AddRegexCommand (regex.c_str(), + subst.c_str()); + } return error; } @@ -1073,89 +1186,6 @@ protected: } } - void - InputReaderDidCancel() - { - m_regex_cmd_ap.reset(); - } - - static size_t - InputReaderCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - { - CommandObjectCommandsAddRegex *add_regex_cmd = (CommandObjectCommandsAddRegex *) baton; - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream (); - out_stream->Printf("%s\n", "Enter regular expressions in the form 's/<regex>/<subst>/' and terminate with an empty line:"); - out_stream->Flush(); - } - break; - case eInputReaderReactivate: - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - while (bytes_len > 0 && (bytes[bytes_len-1] == '\r' || bytes[bytes_len-1] == '\n')) - --bytes_len; - if (bytes_len == 0) - reader.SetIsDone(true); - else if (bytes) - { - llvm::StringRef bytes_strref (bytes, bytes_len); - Error error (add_regex_cmd->AppendRegexSubstitution (bytes_strref)); - if (error.Fail()) - { - if (!batch_mode) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - out_stream->Printf("error: %s\n", error.AsCString()); - out_stream->Flush(); - } - add_regex_cmd->InputReaderDidCancel (); - reader.SetIsDone (true); - } - } - break; - - case eInputReaderInterrupt: - { - reader.SetIsDone (true); - if (!batch_mode) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - out_stream->PutCString("Regular expression command creations was cancelled.\n"); - out_stream->Flush(); - } - add_regex_cmd->InputReaderDidCancel (); - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - add_regex_cmd->AddRegexCommandToInterpreter(); - break; - } - - return bytes_len; - } - private: std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_ap; @@ -1526,7 +1556,9 @@ CommandObjectCommandsScriptImport::CommandOptions::g_option_table[] = // CommandObjectCommandsScriptAdd //------------------------------------------------------------------------- -class CommandObjectCommandsScriptAdd : public CommandObjectParsed +class CommandObjectCommandsScriptAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : @@ -1534,6 +1566,7 @@ public: "command script add", "Add a scripted function as an LLDB command.", NULL), + IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry arg1; @@ -1567,7 +1600,7 @@ protected: public: CommandOptions (CommandInterpreter &interpreter) : - Options (interpreter) + Options (interpreter) { } @@ -1586,7 +1619,7 @@ protected: m_funct_name = std::string(option_arg); break; case 's': - m_synchronous = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); + m_synchronicity = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); if (!error.Success()) error.SetErrorStringWithFormat ("unrecognized value for synchronicity '%s'", option_arg); break; @@ -1602,7 +1635,7 @@ protected: OptionParsingStarting () { m_funct_name = ""; - m_synchronous = eScriptedCommandSynchronicitySynchronous; + m_synchronicity = eScriptedCommandSynchronicitySynchronous; } const OptionDefinition* @@ -1618,128 +1651,81 @@ protected: // Instance variables to hold the values for command options. std::string m_funct_name; - ScriptedCommandSynchronicity m_synchronous; + ScriptedCommandSynchronicity m_synchronicity; }; -private: - class PythonAliasReader : public InputReaderEZ + virtual void + IOHandlerActivated (IOHandler &io_handler) { - private: - CommandInterpreter& m_interpreter; - std::string m_cmd_name; - ScriptedCommandSynchronicity m_synchronous; - StringList m_user_input; - DISALLOW_COPY_AND_ASSIGN (PythonAliasReader); - public: - PythonAliasReader(Debugger& debugger, - CommandInterpreter& interpreter, - std::string cmd_name, - ScriptedCommandSynchronicity synch) : - InputReaderEZ(debugger), - m_interpreter(interpreter), - m_cmd_name(cmd_name), - m_synchronous(synch), - m_user_input() - {} - - virtual - ~PythonAliasReader() + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { + output_sp->PutCString(g_python_command_instructions); + output_sp->Flush(); } + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + StreamFileSP error_sp = io_handler.GetErrorStreamFile(); - virtual void ActivateHandler(HandlerData& data) + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_python_command_instructions); - if (data.reader.GetPrompt()) - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void ReactivateHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.reader.GetPrompt() && !batch_mode) + StringList lines; + lines.SplitIntoLines(data); + if (lines.GetSize() > 0) { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void GotTokenHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.bytes && data.bytes_len) - { - m_user_input.AppendString(data.bytes, data.bytes_len); - } - if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); + std::string funct_name_str; + if (interpreter->GenerateScriptAliasFunction (lines, funct_name_str)) + { + if (funct_name_str.empty()) + { + error_sp->Printf ("error: unable to obtain a function name, didn't add python command.\n"); + error_sp->Flush(); + } + else + { + // everything should be fine now, let's add this alias + + CommandObjectSP command_obj_sp(new CommandObjectPythonFunction (m_interpreter, + m_cmd_name, + funct_name_str.c_str(), + m_synchronicity)); + + if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) + { + error_sp->Printf ("error: unable to add selected command, didn't add python command.\n"); + error_sp->Flush(); + } + } + } + else + { + error_sp->Printf ("error: unable to create function, didn't add python command.\n"); + error_sp->Flush(); + } } - } - virtual void InterruptHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - data.reader.SetIsDone (true); - if (!batch_mode) + else { - out_stream->Printf ("Warning: No script attached.\n"); - out_stream->Flush(); + error_sp->Printf ("error: empty function, didn't add python command.\n"); + error_sp->Flush(); } } - virtual void EOFHandler(HandlerData& data) + else { - data.reader.SetIsDone (true); + error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); + error_sp->Flush(); } - virtual void DoneHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - - ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (!interpreter) - { - out_stream->Printf ("Script interpreter missing: no script attached.\n"); - out_stream->Flush(); - return; - } - std::string funct_name_str; - if (!interpreter->GenerateScriptAliasFunction (m_user_input, - funct_name_str)) - { - out_stream->Printf ("Unable to create function: no script attached.\n"); - out_stream->Flush(); - return; - } - if (funct_name_str.empty()) - { - out_stream->Printf ("Unable to obtain a function name: no script attached.\n"); - out_stream->Flush(); - return; - } - // everything should be fine now, let's add this alias - - CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(m_interpreter, - m_cmd_name, - funct_name_str.c_str(), - m_synchronous)); - - if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) - { - out_stream->Printf ("Unable to add selected command: no script attached.\n"); - out_stream->Flush(); - return; - } - } - }; - + + io_handler.SetIsDone(true); + + + } + protected: bool DoExecute (Args& command, CommandReturnObject &result) @@ -1761,45 +1747,24 @@ protected: return false; } - std::string cmd_name = command.GetArgumentAtIndex(0); + // Store the command name and synchronicity in case we get multi-line input + m_cmd_name = command.GetArgumentAtIndex(0); + m_synchronicity = m_options.m_synchronicity; if (m_options.m_funct_name.empty()) { - InputReaderSP reader_sp (new PythonAliasReader (m_interpreter.GetDebugger(), - m_interpreter, - cmd_name, - m_options.m_synchronous)); - - if (reader_sp) - { - - InputReaderEZ::InitializationParameters ipr; - - Error err (reader_sp->Initialize (ipr.SetBaton(NULL).SetPrompt(" "))); - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); - } + m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + NULL); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions } else { CommandObjectSP new_cmd(new CommandObjectPythonFunction(m_interpreter, - cmd_name, + m_cmd_name, m_options.m_funct_name, - m_options.m_synchronous)); - if (m_interpreter.AddUserCommand(cmd_name, new_cmd, true)) + m_synchronicity)); + if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { result.SetStatus (eReturnStatusSuccessFinishNoResult); } @@ -1815,6 +1780,8 @@ protected: } CommandOptions m_options; + std::string m_cmd_name; + ScriptedCommandSynchronicity m_synchronicity; }; static OptionEnumValueElement g_script_synchro_type[] = diff --git a/source/Commands/CommandObjectDisassemble.cpp b/source/Commands/CommandObjectDisassemble.cpp index fc148b1899f6..f9c683b364ce 100644 --- a/source/Commands/CommandObjectDisassemble.cpp +++ b/source/Commands/CommandObjectDisassemble.cpp @@ -27,6 +27,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" diff --git a/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp index 5ca44ff920d6..c772a2e58912 100644 --- a/source/Commands/CommandObjectExpression.cpp +++ b/source/Commands/CommandObjectExpression.cpp @@ -17,7 +17,6 @@ // Project includes #include "lldb/Interpreter/Args.h" #include "lldb/Core/Value.h" -#include "lldb/Core/InputReader.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Expression/ClangExpressionVariable.h" @@ -197,6 +196,7 @@ CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interprete "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.", NULL, eFlagProcessMustBePaused | eFlagTryTargetAPILock), + IOHandlerDelegate (IOHandlerDelegate::Completion::Expression), m_option_group (interpreter), m_format_options (eFormatDefault), m_command_options (), @@ -254,87 +254,6 @@ CommandObjectExpression::GetOptions () return &m_option_group; } -size_t -CommandObjectExpression::MultiLineExpressionCallback -( - void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len -) -{ - CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton; - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - StreamSP async_strm_sp(reader.GetDebugger().GetAsyncOutputStream()); - if (async_strm_sp) - { - async_strm_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); - async_strm_sp->Flush(); - } - } - // Fall through - case eInputReaderReactivate: - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - ++cmd_object_expr->m_expr_line_count; - if (bytes && bytes_len) - { - cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1); - } - - if (bytes_len == 0) - reader.SetIsDone(true); - break; - - case eInputReaderInterrupt: - cmd_object_expr->m_expr_lines.clear(); - reader.SetIsDone (true); - if (!batch_mode) - { - StreamSP async_strm_sp (reader.GetDebugger().GetAsyncOutputStream()); - if (async_strm_sp) - { - async_strm_sp->PutCString("Expression evaluation cancelled.\n"); - async_strm_sp->Flush(); - } - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - if (cmd_object_expr->m_expr_lines.size() > 0) - { - StreamSP output_stream = reader.GetDebugger().GetAsyncOutputStream(); - StreamSP error_stream = reader.GetDebugger().GetAsyncErrorStream(); - cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), - output_stream.get(), - error_stream.get()); - output_stream->Flush(); - error_stream->Flush(); - } - break; - } - - return bytes_len; -} - bool CommandObjectExpression::EvaluateExpression ( @@ -373,6 +292,8 @@ CommandObjectExpression::EvaluateExpression if (m_command_options.timeout > 0) options.SetTimeoutUsec(m_command_options.timeout); + else + options.SetTimeoutUsec(0); exe_results = target->EvaluateExpression (expr, exe_ctx.GetFramePtr(), @@ -443,6 +364,48 @@ CommandObjectExpression::EvaluateExpression return true; } +void +CommandObjectExpression::IOHandlerInputComplete (IOHandler &io_handler, std::string &line) +{ + io_handler.SetIsDone(true); +// StreamSP output_stream = io_handler.GetDebugger().GetAsyncOutputStream(); +// StreamSP error_stream = io_handler.GetDebugger().GetAsyncErrorStream(); + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + StreamFileSP error_sp(io_handler.GetErrorStreamFile()); + + EvaluateExpression (line.c_str(), + output_sp.get(), + error_sp.get()); + if (output_sp) + output_sp->Flush(); + if (error_sp) + error_sp->Flush(); +} + +LineStatus +CommandObjectExpression::IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error) +{ + if (line_idx == UINT32_MAX) + { + // Remove the last line from "lines" so it doesn't appear + // in our final expression + lines.PopBack(); + error.Clear(); + return LineStatus::Done; + } + else if (line_idx + 1 == lines.GetSize()) + { + // The last line was edited, if this line is empty, then we are done + // getting our multiple lines. + if (lines[line_idx].empty()) + return LineStatus::Done; + } + return LineStatus::Success; +} + bool CommandObjectExpression::DoExecute ( @@ -459,31 +422,21 @@ CommandObjectExpression::DoExecute m_expr_lines.clear(); m_expr_line_count = 0; - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - if (reader_sp) - { - Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback, - this, // baton - eInputReaderGranularityLine, // token size, to pass to callback function - NULL, // end token - NULL, // prompt - true)); // echo input - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + const bool multiple_lines = true; // Get multiple lines + IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + "lldb-expr", // Name of input reader for history + NULL, // No prompt + multiple_lines, + *this)); + + StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile()); + if (output_sp) { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); + output_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); + output_sp->Flush(); } + debugger.PushIOHandler(io_handler_sp); return result.Succeeded(); } diff --git a/source/Commands/CommandObjectExpression.h b/source/Commands/CommandObjectExpression.h index e0703a22a4cc..c943f0e8023d 100644 --- a/source/Commands/CommandObjectExpression.h +++ b/source/Commands/CommandObjectExpression.h @@ -14,6 +14,7 @@ // C++ Includes // Other libraries and framework includes // Project includes +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" @@ -21,7 +22,9 @@ namespace lldb_private { -class CommandObjectExpression : public CommandObjectRaw +class CommandObjectExpression : + public CommandObjectRaw, + public IOHandlerDelegate { public: @@ -71,17 +74,23 @@ public: GetOptions (); protected: + + //------------------------------------------------------------------ + // IOHandler::Delegate functions + //------------------------------------------------------------------ + virtual void + IOHandlerInputComplete (IOHandler &io_handler, + std::string &line); + + virtual LineStatus + IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error); virtual bool DoExecute (const char *command, CommandReturnObject &result); - static size_t - MultiLineExpressionCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - bool EvaluateExpression (const char *expr, Stream *output_stream, diff --git a/source/Commands/CommandObjectGUI.cpp b/source/Commands/CommandObjectGUI.cpp new file mode 100644 index 000000000000..3d05335e92e4 --- /dev/null +++ b/source/Commands/CommandObjectGUI.cpp @@ -0,0 +1,61 @@ +//===-- CommandObjectGUI.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectGUI.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectGUI +//------------------------------------------------------------------------- + +CommandObjectGUI::CommandObjectGUI (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, "gui", "Switch into the curses based GUI mode.", "gui") +{ +} + +CommandObjectGUI::~CommandObjectGUI () +{ +} + +bool +CommandObjectGUI::DoExecute (Args& args, CommandReturnObject &result) +{ +#ifndef LLDB_DISABLE_CURSES + if (args.GetArgumentCount() == 0) + { + Debugger &debugger = m_interpreter.GetDebugger(); + IOHandlerSP io_handler_sp (new IOHandlerCursesGUI (debugger)); + if (io_handler_sp) + debugger.PushIOHandler(io_handler_sp); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError("the gui command takes no arguments."); + result.SetStatus (eReturnStatusFailed); + } + return true; +#else + result.AppendError("lldb was not build with gui support"); + return false; +#endif +} + diff --git a/source/Commands/CommandObjectGUI.h b/source/Commands/CommandObjectGUI.h new file mode 100644 index 000000000000..72ddb961c266 --- /dev/null +++ b/source/Commands/CommandObjectGUI.h @@ -0,0 +1,43 @@ +//===-- CommandObjectGUI.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectGUI_h_ +#define liblldb_CommandObjectGUI_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectGUI +//------------------------------------------------------------------------- + +class CommandObjectGUI : public CommandObjectParsed +{ +public: + + CommandObjectGUI (CommandInterpreter &interpreter); + + virtual + ~CommandObjectGUI (); + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectGUI_h_ diff --git a/source/Commands/CommandObjectMultiword.cpp b/source/Commands/CommandObjectMultiword.cpp index f84b401f3aa6..69b178da46ba 100644 --- a/source/Commands/CommandObjectMultiword.cpp +++ b/source/Commands/CommandObjectMultiword.cpp @@ -235,18 +235,19 @@ CommandObjectMultiword::HandleCompletion // completers will override this. word_complete = true; + const char *arg0 = input.GetArgumentAtIndex(0); if (cursor_index == 0) { CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, - input.GetArgumentAtIndex(0), + arg0, matches); if (matches.GetSize() == 1 && matches.GetStringAtIndex(0) != NULL - && strcmp (input.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + && strcmp (arg0, matches.GetStringAtIndex(0)) == 0) { StringList temp_matches; - CommandObject *cmd_obj = GetSubcommandObject (input.GetArgumentAtIndex(0), + CommandObject *cmd_obj = GetSubcommandObject (arg0, &temp_matches); if (cmd_obj != NULL) { @@ -270,7 +271,7 @@ CommandObjectMultiword::HandleCompletion } else { - CommandObject *sub_command_object = GetSubcommandObject (input.GetArgumentAtIndex(0), + CommandObject *sub_command_object = GetSubcommandObject (arg0, &matches); if (sub_command_object == NULL) { diff --git a/source/Commands/CommandObjectProcess.cpp b/source/Commands/CommandObjectProcess.cpp index 2933c78ca908..49a392286c6a 100644 --- a/source/Commands/CommandObjectProcess.cpp +++ b/source/Commands/CommandObjectProcess.cpp @@ -49,7 +49,7 @@ public: virtual ~CommandObjectProcessLaunchOrAttach () {} protected: bool - StopProcessIfNecessary (Process *&process, StateType &state, CommandReturnObject &result) + StopProcessIfNecessary (Process *process, StateType &state, CommandReturnObject &result) { state = eStateInvalid; if (process) @@ -187,12 +187,10 @@ protected: { Debugger &debugger = m_interpreter.GetDebugger(); Target *target = debugger.GetSelectedTarget().get(); - Error error; // If our listener is NULL, users aren't allows to launch - char filename[PATH_MAX]; - const Module *exe_module = target->GetExecutableModulePointer(); + ModuleSP exe_module_sp = target->GetExecutableModule(); - if (exe_module == NULL) + if (exe_module_sp == NULL) { result.AppendError ("no file in target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); @@ -200,23 +198,31 @@ protected: } StateType state = eStateInvalid; - Process *process = m_exe_ctx.GetProcessPtr(); - if (!StopProcessIfNecessary(process, state, result)) + if (!StopProcessIfNecessary(m_exe_ctx.GetProcessPtr(), state, result)) return false; const char *target_settings_argv0 = target->GetArg0(); - exe_module->GetFileSpec().GetPath (filename, sizeof(filename)); + if (target->GetDisableASLR()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + + if (target->GetDisableSTDIO()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); + Args environment; + target->GetEnvironmentAsArgs (environment); + if (environment.GetArgumentCount() > 0) + m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment); + if (target_settings_argv0) { m_options.launch_info.GetArguments().AppendArgument (target_settings_argv0); - m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), false); + m_options.launch_info.SetExecutableFile(exe_module_sp->GetPlatformFileSpec(), false); } else { - m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + m_options.launch_info.SetExecutableFile(exe_module_sp->GetPlatformFileSpec(), true); } if (launch_args.GetArgumentCount() == 0) @@ -228,122 +234,33 @@ protected: else { m_options.launch_info.GetArguments().AppendArguments (launch_args); - // Save the arguments for subsequent runs in the current target. target->SetRunArguments (launch_args); } - if (target->GetDisableASLR()) - m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); - - if (target->GetDisableSTDIO()) - m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); - - m_options.launch_info.GetFlags().Set (eLaunchFlagDebug); - - Args environment; - target->GetEnvironmentAsArgs (environment); - if (environment.GetArgumentCount() > 0) - m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment); - - // Get the value of synchronous execution here. If you wait till after you have started to - // run, then you could have hit a breakpoint, whose command might switch the value, and - // then you'll pick up that incorrect value. - bool synchronous_execution = m_interpreter.GetSynchronous (); - - PlatformSP platform_sp (target->GetPlatform()); - - // Finalize the file actions, and if none were given, default to opening - // up a pseudo terminal - const bool default_to_use_pty = platform_sp ? platform_sp->IsHost() : false; - m_options.launch_info.FinalizeFileActions (target, default_to_use_pty); - - if (state == eStateConnected) - { - if (m_options.launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) - { - result.AppendWarning("can't launch in tty when launching through a remote connection"); - m_options.launch_info.GetFlags().Clear (eLaunchFlagLaunchInTTY); - } - } - - if (!m_options.launch_info.GetArchitecture().IsValid()) - m_options.launch_info.GetArchitecture() = target->GetArchitecture(); + Error error = target->Launch(debugger.GetListener(), m_options.launch_info); - if (platform_sp && platform_sp->CanDebugProcess ()) - { - process = target->GetPlatform()->DebugProcess (m_options.launch_info, - debugger, - target, - debugger.GetListener(), - error).get(); - } - else - { - const char *plugin_name = m_options.launch_info.GetProcessPluginName(); - process = target->CreateProcess (debugger.GetListener(), plugin_name, NULL).get(); - if (process) - error = process->Launch (m_options.launch_info); - } - - if (process == NULL) - { - result.SetError (error, "failed to launch or debug process"); - return false; - } - - if (error.Success()) { - const char *archname = exe_module->GetArchitecture().GetArchitectureName(); - - result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process->GetID(), filename, archname); - result.SetDidChangeProcessState (true); - if (m_options.launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == false) + const char *archname = exe_module_sp->GetArchitecture().GetArchitectureName(); + ProcessSP process_sp (target->GetProcessSP()); + if (process_sp) { - result.SetStatus (eReturnStatusSuccessContinuingNoResult); - StateType state = process->WaitForProcessToStop (NULL, NULL, false); - - if (state == eStateStopped) - { - error = process->Resume(); - if (error.Success()) - { - if (synchronous_execution) - { - state = process->WaitForProcessToStop (NULL); - const bool must_be_alive = true; - if (!StateIsStoppedState(state, must_be_alive)) - { - result.AppendErrorWithFormat ("process isn't stopped: %s", StateAsCString(state)); - } - result.SetDidChangeProcessState (true); - result.SetStatus (eReturnStatusSuccessFinishResult); - } - else - { - result.SetStatus (eReturnStatusSuccessContinuingNoResult); - } - } - else - { - result.AppendErrorWithFormat ("process resume at entry point failed: %s", error.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendErrorWithFormat ("initial process state wasn't stopped: %s", StateAsCString(state)); - result.SetStatus (eReturnStatusFailed); - } + result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(), exe_module_sp->GetFileSpec().GetPath().c_str(), archname); + result.SetStatus (eReturnStatusSuccessFinishResult); + result.SetDidChangeProcessState (true); + } + else + { + result.AppendError("no error returned from Target::Launch, and target has no process"); + result.SetStatus (eReturnStatusFailed); } } else { - result.AppendErrorWithFormat ("process launch failed: %s", error.AsCString()); + result.AppendError(error.AsCString()); result.SetStatus (eReturnStatusFailed); } - return result.Succeeded(); } @@ -615,37 +532,36 @@ protected: if (error.Success()) { + ListenerSP listener_sp (new Listener("lldb.CommandObjectProcessAttach.DoExecute.attach.hijack")); + m_options.attach_info.SetHijackListener(listener_sp); + process->HijackProcessEvents(listener_sp.get()); error = process->Attach (m_options.attach_info); if (error.Success()) { result.SetStatus (eReturnStatusSuccessContinuingNoResult); - } - else - { - result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString()); - result.SetStatus (eReturnStatusFailed); - return false; - } - // If we're synchronous, wait for the stopped event and report that. - // Otherwise just return. - // FIXME: in the async case it will now be possible to get to the command - // interpreter with a state eStateAttaching. Make sure we handle that correctly. - StateType state = process->WaitForProcessToStop (NULL); - - result.SetDidChangeProcessState (true); + StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); - if (state == eStateStopped) - { - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); - result.SetStatus (eReturnStatusSuccessFinishNoResult); + process->RestoreProcessEvents(); + + result.SetDidChangeProcessState (true); + + if (state == eStateStopped) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("attach failed: process did not stop (no such process or permission problem?)"); + process->Destroy(); + result.SetStatus (eReturnStatusFailed); + } } else { - result.AppendError ("attach failed: process did not stop (no such process or permission problem?)"); - process->Destroy(); + result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString()); result.SetStatus (eReturnStatusFailed); - return false; } } } @@ -1170,7 +1086,7 @@ protected: if (process) { - error = process->ConnectRemote (&process->GetTarget().GetDebugger().GetOutputStream(), remote_url); + error = process->ConnectRemote (process->GetTarget().GetDebugger().GetOutputFile().get(), remote_url); if (error.Fail()) { diff --git a/source/Commands/CommandObjectQuit.cpp b/source/Commands/CommandObjectQuit.cpp index d04ecdd9885c..ffe2a9240726 100644 --- a/source/Commands/CommandObjectQuit.cpp +++ b/source/Commands/CommandObjectQuit.cpp @@ -92,7 +92,8 @@ CommandObjectQuit::DoExecute (Args& command, CommandReturnObject &result) return false; } } - m_interpreter.BroadcastEvent (CommandInterpreter::eBroadcastBitQuitCommandReceived); + const uint32_t event_type = CommandInterpreter::eBroadcastBitQuitCommandReceived; + m_interpreter.BroadcastEvent (event_type); result.SetStatus (eReturnStatusQuit); return true; } diff --git a/source/Commands/CommandObjectRegister.cpp b/source/Commands/CommandObjectRegister.cpp index 7cbfaa5d60fc..deaf2ab3793e 100644 --- a/source/Commands/CommandObjectRegister.cpp +++ b/source/Commands/CommandObjectRegister.cpp @@ -29,6 +29,7 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Thread.h" using namespace lldb; diff --git a/source/Commands/CommandObjectSource.cpp b/source/Commands/CommandObjectSource.cpp index 1f6873611a22..bf2a42e0bea0 100644 --- a/source/Commands/CommandObjectSource.cpp +++ b/source/Commands/CommandObjectSource.cpp @@ -28,6 +28,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/TargetList.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/Options.h" diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index 8e7e68aad39a..308b72f355d3 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -19,7 +19,7 @@ // Project includes #include "lldb/Interpreter/Args.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/InputReader.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" @@ -49,6 +49,7 @@ #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" @@ -2893,7 +2894,8 @@ protected: if (m_slide_option.GetOptionValue().OptionWasSet()) { const addr_t slide = m_slide_option.GetOptionValue().GetCurrentValue(); - module->SetLoadAddress (*target, slide, changed); + const bool slide_is_offset = true; + module->SetLoadAddress (*target, slide, slide_is_offset, changed); } else { @@ -4758,7 +4760,9 @@ private: // CommandObjectTargetStopHookAdd //------------------------------------------------------------------------- -class CommandObjectTargetStopHookAdd : public CommandObjectParsed +class CommandObjectTargetStopHookAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: @@ -4925,9 +4929,10 @@ public: CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, - "target stop-hook add ", + "target stop-hook add", "Add a hook to be executed when the target stops.", "target stop-hook add"), + IOHandlerDelegateMultiline ("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { } @@ -4936,102 +4941,61 @@ public: { } - static size_t - ReadCommandsCallbackFunction (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) +protected: + + virtual void + IOHandlerActivated (IOHandler &io_handler) { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - Target::StopHook *new_stop_hook = ((Target::StopHook *) baton); - static bool got_interrupted; - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - case eInputReaderActivate: - if (!batch_mode) - { - out_stream->Printf ("%s\n", "Enter your stop hook command(s). Type 'DONE' to end."); - if (reader.GetPrompt()) - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - got_interrupted = false; - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderReactivate: - if (reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - got_interrupted = false; - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - if (bytes && bytes_len && baton) + output_sp->PutCString("Enter your stop hook command(s). Type 'DONE' to end.\n"); + output_sp->Flush(); + } + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &line) + { + if (m_stop_hook_sp) + { + if (line.empty()) { - StringList *commands = new_stop_hook->GetCommandPointer(); - if (commands) + StreamFileSP error_sp(io_handler.GetErrorStreamFile()); + if (error_sp) { - commands->AppendString (bytes, bytes_len); + error_sp->Printf("error: stop hook #%" PRIu64 " aborted, no commands.\n", m_stop_hook_sp->GetID()); + error_sp->Flush(); } + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + target->RemoveStopHookByID(m_stop_hook_sp->GetID()); } - if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderInterrupt: + else { - // Finish, and cancel the stop hook. - new_stop_hook->GetTarget()->RemoveStopHookByID(new_stop_hook->GetID()); - if (!batch_mode) + m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line); + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - out_stream->Printf ("Stop hook cancelled.\n"); - out_stream->Flush(); + output_sp->Printf("Stop hook #%" PRIu64 " added.\n", m_stop_hook_sp->GetID()); + output_sp->Flush(); } - - reader.SetIsDone (true); } - got_interrupted = true; - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - if (!got_interrupted && !batch_mode) - { - out_stream->Printf ("Stop hook #%" PRIu64 " added.\n", new_stop_hook->GetID()); - out_stream->Flush(); - } - break; + m_stop_hook_sp.reset(); } - - return bytes_len; + io_handler.SetIsDone(true); } - -protected: + bool DoExecute (Args& command, CommandReturnObject &result) { + m_stop_hook_sp.reset(); + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { - Target::StopHookSP new_hook_sp; - target->AddStopHook (new_hook_sp); + Target::StopHookSP new_hook_sp = target->CreateStopHook(); // First step, make the specifier. std::unique_ptr<SymbolContextSpecifier> specifier_ap; @@ -5104,31 +5068,12 @@ protected: } else { - // Otherwise gather up the command list, we'll push an input reader and suck the data from that directly into - // the new stop hook's command string. - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - if (!reader_sp) - { - result.AppendError("out of memory\n"); - result.SetStatus (eReturnStatusFailed); - target->RemoveStopHookByID (new_hook_sp->GetID()); - return false; - } - - Error err (reader_sp->Initialize (CommandObjectTargetStopHookAdd::ReadCommandsCallbackFunction, - new_hook_sp.get(), // baton - eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token - "> ", // prompt - true)); // echo input - if (!err.Success()) - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - target->RemoveStopHookByID (new_hook_sp->GetID()); - return false; - } - m_interpreter.GetDebugger().PushInputReader (reader_sp); + m_stop_hook_sp = new_hook_sp; + m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + NULL); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + } result.SetStatus (eReturnStatusSuccessFinishNoResult); } @@ -5142,6 +5087,7 @@ protected: } private: CommandOptions m_options; + Target::StopHookSP m_stop_hook_sp; }; OptionDefinition diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp index 1c695b37c566..f1b1d2c1900c 100644 --- a/source/Commands/CommandObjectType.cpp +++ b/source/Commands/CommandObjectType.cpp @@ -19,7 +19,7 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/State.h" #include "lldb/Core/StringList.h" @@ -42,7 +42,6 @@ public: TypeSummaryImpl::Flags m_flags; StringList m_target_types; - StringList m_user_source; bool m_regex; @@ -74,7 +73,6 @@ public: bool m_skip_references; bool m_cascade; bool m_regex; - StringList m_user_source; StringList m_target_types; std::string m_category; @@ -88,7 +86,6 @@ public: m_skip_references(sref), m_cascade(casc), m_regex(regx), - m_user_source(), m_target_types(), m_category(catg) { @@ -98,9 +95,36 @@ public: }; +static bool +WarnOnPotentialUnquotedUnsignedType (Args& command, CommandReturnObject &result) +{ + for (int idx = 0; idx < command.GetArgumentCount(); idx++) + { + const char* arg = command.GetArgumentAtIndex(idx); + if (idx+1 < command.GetArgumentCount()) + { + if (arg && 0 == strcmp(arg,"unsigned")) + { + const char* next = command.GetArgumentAtIndex(idx+1); + if (next && + (0 == strcmp(next, "int") || + 0 == strcmp(next, "short") || + 0 == strcmp(next, "char") || + 0 == strcmp(next, "long"))) + { + result.AppendWarningWithFormat("%s %s being treated as two types. if you meant the combined type name use quotes, as in \"%s %s\"\n", + arg,next,arg,next); + return true; + } + } + } + } + return false; +} - -class CommandObjectTypeSummaryAdd : public CommandObjectParsed +class CommandObjectTypeSummaryAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { private: @@ -153,10 +177,6 @@ private: return &m_options; } - void - CollectPythonScript(ScriptAddOptions *options, - CommandReturnObject &result); - bool Execute_ScriptSummary (Args& command, CommandReturnObject &result); @@ -178,6 +198,147 @@ public: { } + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function (valobj,internal_dict):\n" + " \"\"\"valobj: an SBValue which you want to provide a summary for\n" + " internal_dict: an LLDB support object not to be used\"\"\""; + + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) + { + output_sp->PutCString(g_summary_addreader_instructions); + output_sp->Flush(); + } + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + StreamFileSP error_sp = io_handler.GetErrorStreamFile(); + +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + StringList lines; + lines.SplitIntoLines(data); + if (lines.GetSize() > 0) + { + ScriptAddOptions *options_ptr = ((ScriptAddOptions*)io_handler.GetUserData()); + if (options_ptr) + { + ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + std::string funct_name_str; + if (interpreter->GenerateTypeScriptFunction (lines, funct_name_str)) + { + if (funct_name_str.empty()) + { + error_sp->Printf ("unable to obtain a valid function name from the script interpreter.\n"); + error_sp->Flush(); + } + else + { + // now I have a valid function name, let's add this as script for every type in the list + + TypeSummaryImplSP script_format; + script_format.reset(new ScriptSummaryFormat(options->m_flags, + funct_name_str.c_str(), + lines.CopyList(" ").c_str())); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), + script_format, + (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), + options->m_category, + &error); + if (error.Fail()) + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + + if (options->m_name) + { + CommandObjectTypeSummaryAdd::AddSummary (options->m_name, + script_format, + CommandObjectTypeSummaryAdd::eNamedSummary, + options->m_category, + &error); + if (error.Fail()) + { + CommandObjectTypeSummaryAdd::AddSummary (options->m_name, + script_format, + CommandObjectTypeSummaryAdd::eNamedSummary, + options->m_category, + &error); + if (error.Fail()) + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + else + { + if (error.AsCString()) + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + } + } + else + { + error_sp->Printf ("error: unable to generate a function.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: no script interpreter.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: internal synchronization information missing or invalid.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: empty function, didn't add python command.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); + error_sp->Flush(); + } +#endif // #ifndef LLDB_DISABLE_PYTHON + io_handler.SetIsDone(true); + } + static bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry, @@ -190,7 +351,19 @@ protected: }; -class CommandObjectTypeSynthAdd : public CommandObjectParsed +static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" +"You must define a Python class with these methods:\n" +" def __init__(self, valobj, dict):\n" +" def num_children(self):\n" +" def get_child_at_index(self, index):\n" +" def get_child_index(self, name):\n" +" def update(self):\n" +" '''Optional'''\n" +"class synthProvider:\n"; + +class CommandObjectTypeSynthAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { private: @@ -200,7 +373,7 @@ private: public: CommandOptions (CommandInterpreter &interpreter) : - Options (interpreter) + Options (interpreter) { } @@ -296,9 +469,6 @@ private: return &m_options; } - void - CollectPythonScript (SynthAddOptions *options, - CommandReturnObject &result); bool Execute_HandwritePython (Args& command, CommandReturnObject &result); @@ -307,8 +477,139 @@ private: protected: bool - DoExecute (Args& command, CommandReturnObject &result); + DoExecute (Args& command, CommandReturnObject &result) + { + WarnOnPotentialUnquotedUnsignedType(command, result); + + if (m_options.handwrite_python) + return Execute_HandwritePython(command, result); + else if (m_options.is_class_based) + return Execute_PythonClass(command, result); + else + { + result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) + { + output_sp->PutCString(g_synth_addreader_instructions); + output_sp->Flush(); + } + } + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + StreamFileSP error_sp = io_handler.GetErrorStreamFile(); + +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + StringList lines; + lines.SplitIntoLines(data); + if (lines.GetSize() > 0) + { + SynthAddOptions *options_ptr = ((SynthAddOptions*)io_handler.GetUserData()); + if (options_ptr) + { + SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + std::string class_name_str; + if (interpreter->GenerateTypeSynthClass (lines, class_name_str)) + { + if (class_name_str.empty()) + { + error_sp->Printf ("error: unable to obtain a proper name for the class.\n"); + error_sp->Flush(); + } + else + { + // everything should be fine now, let's add the synth provider class + + SyntheticChildrenSP synth_provider; + synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). + SetSkipPointers(options->m_skip_pointers). + SetSkipReferences(options->m_skip_references), + class_name_str.c_str())); + + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + ConstString const_type_name(type_name); + if (const_type_name) + { + if (!CommandObjectTypeSynthAdd::AddSynth(const_type_name, + synth_provider, + options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, + options->m_category, + &error)) + { + error_sp->Printf("error: %s\n", error.AsCString()); + error_sp->Flush(); + break; + } + } + else + { + error_sp->Printf ("error: invalid type name.\n"); + error_sp->Flush(); + break; + } + } + } + } + else + { + error_sp->Printf ("error: unable to generate a class.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: no script interpreter.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: internal synchronization data missing.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: empty function, didn't add python command.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); + error_sp->Flush(); + } + +#endif // #ifndef LLDB_DISABLE_PYTHON + io_handler.SetIsDone(true); + } + public: enum SynthFormatType @@ -371,6 +672,7 @@ private: m_skip_references = false; m_regex = false; m_category.assign("default"); + m_custom_type_name.clear(); } virtual Error SetOptionValue (CommandInterpreter &interpreter, @@ -400,6 +702,9 @@ private: case 'x': m_regex = true; break; + case 't': + m_custom_type_name.assign(option_value); + break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; @@ -419,6 +724,7 @@ private: bool m_skip_pointers; bool m_regex; std::string m_category; + std::string m_custom_type_name; }; OptionGroupOptions m_option_group; @@ -480,7 +786,7 @@ public: ); // Add the "--format" to all options groups - m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_ALL); + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1); m_option_group.Append (&m_command_options); m_option_group.Finalize(); @@ -504,7 +810,7 @@ protected: } const Format format = m_format_options.GetFormat(); - if (format == eFormatInvalid) + if (format == eFormatInvalid && m_command_options.m_custom_type_name.empty()) { result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); @@ -513,10 +819,16 @@ protected: TypeFormatImplSP entry; - entry.reset(new TypeFormatImpl(format, - TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). - SetSkipPointers(m_command_options.m_skip_pointers). - SetSkipReferences(m_command_options.m_skip_references))); + if (m_command_options.m_custom_type_name.empty()) + entry.reset(new TypeFormatImpl_Format(format, + TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). + SetSkipPointers(m_command_options.m_skip_pointers). + SetSkipReferences(m_command_options.m_skip_references))); + else + entry.reset(new TypeFormatImpl_EnumType(ConstString(m_command_options.m_custom_type_name.c_str()), + TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). + SetSkipPointers(m_command_options.m_skip_pointers). + SetSkipReferences(m_command_options.m_skip_references))); // now I have a valid format, let's add it to every type @@ -525,6 +837,8 @@ protected: if (!category_sp) return false; + WarnOnPotentialUnquotedUnsignedType(command, result); + for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); @@ -540,11 +854,11 @@ protected: result.SetStatus(eReturnStatusFailed); return false; } - category_sp->GetRegexSummaryNavigator()->Delete(typeCS); - category_sp->GetRegexValueNavigator()->Add(typeRX, entry); + category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS); + category_sp->GetRegexTypeFormatsContainer()->Add(typeRX, entry); } else - category_sp->GetValueNavigator()->Add(typeCS, entry); + category_sp->GetTypeFormatsContainer()->Add(typeCS, entry); } else { @@ -562,11 +876,12 @@ protected: OptionDefinition CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, - { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, - { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, - { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { LLDB_OPT_SET_2, false, "type", 't', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Format variables as if they were of this type."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; @@ -817,8 +1132,8 @@ private: PerCategoryCallback(void* param, const lldb::TypeCategoryImplSP& cate) { - cate->GetValueNavigator()->Clear(); - cate->GetRegexValueNavigator()->Clear(); + cate->GetTypeFormatsContainer()->Clear(); + cate->GetRegexTypeFormatsContainer()->Clear(); return true; } @@ -996,6 +1311,7 @@ protected: param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + delete param; if (cate_regex) delete cate_regex; @@ -1029,12 +1345,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetValueNavigator()->LoopThrough(CommandObjectTypeFormatList_LoopCallback, param_vp); + cate->GetTypeFormatsContainer()->LoopThrough(CommandObjectTypeFormatList_LoopCallback, param_vp); - if (cate->GetRegexSummaryNavigator()->GetCount() > 0) + if (cate->GetRegexTypeSummariesContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); - cate->GetRegexValueNavigator()->LoopThrough(CommandObjectTypeRXFormatList_LoopCallback, param_vp); + cate->GetRegexTypeFormatsContainer()->LoopThrough(CommandObjectTypeRXFormatList_LoopCallback, param_vp); } return true; } @@ -1089,176 +1405,6 @@ CommandObjectTypeFormatList::CommandOptions::g_option_table[] = // CommandObjectTypeSummaryAdd //------------------------------------------------------------------------- -static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" - "def function (valobj,internal_dict):\n" - " \"\"\"valobj: an SBValue which you want to provide a summary for\n" - " internal_dict: an LLDB support object not to be used\"\"\""; - -class TypeScriptAddInputReader : public InputReaderEZ -{ -private: - DISALLOW_COPY_AND_ASSIGN (TypeScriptAddInputReader); -public: - TypeScriptAddInputReader(Debugger& debugger) : - InputReaderEZ(debugger) - {} - - virtual - ~TypeScriptAddInputReader() - { - } - - virtual void ActivateHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_summary_addreader_instructions); - if (data.reader.GetPrompt()) - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - - virtual void ReactivateHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - if (data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void GotTokenHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - if (data.bytes && data.bytes_len && data.baton) - { - ((ScriptAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); - } - if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void InterruptHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - data.reader.SetIsDone (true); - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to breakpoint.\n"); - out_stream->Flush(); - } - } - virtual void EOFHandler(HandlerData& data) - { - data.reader.SetIsDone (true); - } - virtual void DoneHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - ScriptAddOptions *options_ptr = ((ScriptAddOptions*)data.baton); - if (!options_ptr) - { - out_stream->Printf ("internal synchronization information missing or invalid.\n"); - out_stream->Flush(); - return; - } - - ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope - - ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (!interpreter) - { - out_stream->Printf ("no script interpreter.\n"); - out_stream->Flush(); - return; - } - std::string funct_name_str; - if (!interpreter->GenerateTypeScriptFunction (options->m_user_source, - funct_name_str)) - { - out_stream->Printf ("unable to generate a function.\n"); - out_stream->Flush(); - return; - } - if (funct_name_str.empty()) - { - out_stream->Printf ("unable to obtain a valid function name from the script interpreter.\n"); - out_stream->Flush(); - return; - } - // now I have a valid function name, let's add this as script for every type in the list - - TypeSummaryImplSP script_format; - script_format.reset(new ScriptSummaryFormat(options->m_flags, - funct_name_str.c_str(), - options->m_user_source.CopyList(" ").c_str())); - - Error error; - - for (size_t i = 0; i < options->m_target_types.GetSize(); i++) - { - const char *type_name = options->m_target_types.GetStringAtIndex(i); - CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), - script_format, - (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), - options->m_category, - &error); - if (error.Fail()) - { - out_stream->Printf ("%s", error.AsCString()); - out_stream->Flush(); - return; - } - } - - if (options->m_name) - { - CommandObjectTypeSummaryAdd::AddSummary (options->m_name, - script_format, - CommandObjectTypeSummaryAdd::eNamedSummary, - options->m_category, - &error); - if (error.Fail()) - { - CommandObjectTypeSummaryAdd::AddSummary (options->m_name, - script_format, - CommandObjectTypeSummaryAdd::eNamedSummary, - options->m_category, - &error); - if (error.Fail()) - { - out_stream->Printf ("%s", error.AsCString()); - out_stream->Flush(); - return; - } - } - else - { - out_stream->Printf ("%s", error.AsCString()); - out_stream->Flush(); - return; - } - } - else - { - if (error.AsCString()) - { - out_stream->PutCString (error.AsCString()); - out_stream->Flush(); - } - return; - } - } -}; - #endif // #ifndef LLDB_DISABLE_PYTHON Error @@ -1339,35 +1485,9 @@ CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting () m_category = "default"; } + + #ifndef LLDB_DISABLE_PYTHON -void -CommandObjectTypeSummaryAdd::CollectPythonScript (ScriptAddOptions *options, - CommandReturnObject &result) -{ - InputReaderSP reader_sp (new TypeScriptAddInputReader(m_interpreter.GetDebugger())); - if (reader_sp && options) - { - - InputReaderEZ::InitializationParameters ipr; - - Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); - } -} bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturnObject &result) @@ -1393,7 +1513,7 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn return false; } - std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); + std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); script_format.reset(new ScriptSummaryFormat(m_options.m_flags, funct_name, @@ -1432,14 +1552,15 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn return false; } - std::string code = " " + m_options.m_python_script; + std::string code = " " + m_options.m_python_script; script_format.reset(new ScriptSummaryFormat(m_options.m_flags, funct_name_str.c_str(), code.c_str())); } - else // use an InputReader to grab Python code from the user - { + else + { + // Use an IOHandler to grab Python code from the user ScriptAddOptions *options = new ScriptAddOptions(m_options.m_flags, m_options.m_regex, m_options.m_name, @@ -1458,7 +1579,12 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn } } - CollectPythonScript(options,result); + m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); } @@ -1590,6 +1716,7 @@ CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &in "type summary add", "Add a new summary style for a type.", NULL), + IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry type_arg; @@ -1671,6 +1798,8 @@ CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &in bool CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &result) { + WarnOnPotentialUnquotedUnsignedType(command, result); + if (m_options.m_is_add_script) { #ifndef LLDB_DISABLE_PYTHON @@ -1720,8 +1849,8 @@ CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, return false; } - category->GetRegexSummaryNavigator()->Delete(type_name); - category->GetRegexSummaryNavigator()->Add(typeRX, entry); + category->GetRegexTypeSummariesContainer()->Delete(type_name); + category->GetRegexTypeSummariesContainer()->Add(typeRX, entry); return true; } @@ -1733,7 +1862,7 @@ CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, } else { - category->GetSummaryNavigator()->Add(type_name, entry); + category->GetTypeSummariesContainer()->Add(type_name, entry); return true; } } @@ -1994,8 +2123,8 @@ private: PerCategoryCallback(void* param, const lldb::TypeCategoryImplSP& cate) { - cate->GetSummaryNavigator()->Clear(); - cate->GetRegexSummaryNavigator()->Clear(); + cate->GetTypeSummariesContainer()->Clear(); + cate->GetRegexTypeSummariesContainer()->Clear(); return true; } @@ -2178,7 +2307,8 @@ protected: param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); - + delete param; + if (DataVisualization::NamedSummaryFormats::GetCount() > 0) { result.GetOutputStream().Printf("Named summaries:\n"); @@ -2226,12 +2356,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetSummaryNavigator()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param_vp); + cate->GetTypeSummariesContainer()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param_vp); - if (cate->GetRegexSummaryNavigator()->GetCount() > 0) + if (cate->GetRegexTypeSummariesContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); - cate->GetRegexSummaryNavigator()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param_vp); + cate->GetRegexTypeSummariesContainer()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param_vp); } return true; } @@ -2741,6 +2871,7 @@ protected: param = new CommandObjectTypeFilterList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + delete param; if (cate_regex) delete cate_regex; @@ -2774,12 +2905,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetFilterNavigator()->LoopThrough(CommandObjectTypeFilterList_LoopCallback, param_vp); + cate->GetTypeFiltersContainer()->LoopThrough(CommandObjectTypeFilterList_LoopCallback, param_vp); - if (cate->GetRegexFilterNavigator()->GetCount() > 0) + if (cate->GetRegexTypeFiltersContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based filters (slower):\n"); - cate->GetRegexFilterNavigator()->LoopThrough(CommandObjectTypeFilterRXList_LoopCallback, param_vp); + cate->GetRegexTypeFiltersContainer()->LoopThrough(CommandObjectTypeFilterRXList_LoopCallback, param_vp); } return true; @@ -2955,7 +3086,8 @@ protected: param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); - + delete param; + if (cate_regex) delete cate_regex; @@ -2988,12 +3120,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp); + cate->GetTypeSyntheticsContainer()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp); - if (cate->GetRegexSyntheticNavigator()->GetCount() > 0) + if (cate->GetRegexTypeSyntheticsContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based synthetic providers (slower):\n"); - cate->GetRegexSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthRXList_LoopCallback, param_vp); + cate->GetRegexTypeSyntheticsContainer()->LoopThrough(CommandObjectTypeSynthRXList_LoopCallback, param_vp); } return true; @@ -3179,8 +3311,8 @@ protected: lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); - bool delete_category = category->GetFilterNavigator()->Delete(typeCS); - delete_category = category->GetRegexFilterNavigator()->Delete(typeCS) || delete_category; + bool delete_category = category->GetTypeFiltersContainer()->Delete(typeCS); + delete_category = category->GetRegexTypeFiltersContainer()->Delete(typeCS) || delete_category; if (delete_category) { @@ -3345,8 +3477,8 @@ protected: lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); - bool delete_category = category->GetSyntheticNavigator()->Delete(typeCS); - delete_category = category->GetRegexSyntheticNavigator()->Delete(typeCS) || delete_category; + bool delete_category = category->GetTypeSyntheticsContainer()->Delete(typeCS); + delete_category = category->GetRegexTypeSyntheticsContainer()->Delete(typeCS) || delete_category; if (delete_category) { @@ -3484,8 +3616,8 @@ protected: } else DataVisualization::Categories::GetCategory(ConstString(NULL), category); - category->GetFilterNavigator()->Clear(); - category->GetRegexFilterNavigator()->Clear(); + category->GetTypeFiltersContainer()->Clear(); + category->GetRegexTypeFiltersContainer()->Clear(); } result.SetStatus(eReturnStatusSuccessFinishResult); @@ -3613,8 +3745,8 @@ protected: } else DataVisualization::Categories::GetCategory(ConstString(NULL), category); - category->GetSyntheticNavigator()->Clear(); - category->GetRegexSyntheticNavigator()->Clear(); + category->GetTypeSyntheticsContainer()->Clear(); + category->GetRegexTypeSyntheticsContainer()->Clear(); } result.SetStatus(eReturnStatusSuccessFinishResult); @@ -3631,193 +3763,6 @@ CommandObjectTypeSynthClear::CommandOptions::g_option_table[] = }; -//------------------------------------------------------------------------- -// TypeSynthAddInputReader -//------------------------------------------------------------------------- - -static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" - "You must define a Python class with these methods:\n" - " def __init__(self, valobj, dict):\n" - " def num_children(self):\n" - " def get_child_at_index(self, index):\n" - " def get_child_index(self, name):\n" - "Optionally, you can also define a method:\n" - " def update(self):\n" - "if your synthetic provider is holding on to any per-object state variables (currently, this is not implemented because of the way LLDB handles instances of SBValue and you should not rely on object persistence and per-object state)\n" - "class synthProvider:"; - -class TypeSynthAddInputReader : public InputReaderEZ -{ -public: - TypeSynthAddInputReader(Debugger& debugger) : - InputReaderEZ(debugger) - {} - - virtual - ~TypeSynthAddInputReader() - { - } - - virtual void ActivateHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_synth_addreader_instructions); - if (data.reader.GetPrompt()) - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - - virtual void ReactivateHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void GotTokenHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.bytes && data.bytes_len && data.baton) - { - ((SynthAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); - } - if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void InterruptHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - data.reader.SetIsDone (true); - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to breakpoint.\n"); - out_stream->Flush(); - } - } - virtual void EOFHandler(HandlerData& data) - { - data.reader.SetIsDone (true); - } - virtual void DoneHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - SynthAddOptions *options_ptr = ((SynthAddOptions*)data.baton); - if (!options_ptr) - { - out_stream->Printf ("internal synchronization data missing.\n"); - out_stream->Flush(); - return; - } - - SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope - - ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (!interpreter) - { - out_stream->Printf ("no script interpreter.\n"); - out_stream->Flush(); - return; - } - std::string class_name_str; - if (!interpreter->GenerateTypeSynthClass (options->m_user_source, - class_name_str)) - { - out_stream->Printf ("unable to generate a class.\n"); - out_stream->Flush(); - return; - } - if (class_name_str.empty()) - { - out_stream->Printf ("unable to obtain a proper name for the class.\n"); - out_stream->Flush(); - return; - } - - // everything should be fine now, let's add the synth provider class - - SyntheticChildrenSP synth_provider; - synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). - SetSkipPointers(options->m_skip_pointers). - SetSkipReferences(options->m_skip_references), - class_name_str.c_str())); - - - lldb::TypeCategoryImplSP category; - DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); - - Error error; - - for (size_t i = 0; i < options->m_target_types.GetSize(); i++) - { - const char *type_name = options->m_target_types.GetStringAtIndex(i); - ConstString typeCS(type_name); - if (typeCS) - { - if (!CommandObjectTypeSynthAdd::AddSynth(typeCS, - synth_provider, - options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, - options->m_category, - &error)) - { - out_stream->Printf("%s\n", error.AsCString()); - out_stream->Flush(); - return; - } - } - else - { - out_stream->Printf ("invalid type name.\n"); - out_stream->Flush(); - return; - } - } - } - -private: - DISALLOW_COPY_AND_ASSIGN (TypeSynthAddInputReader); -}; - -void -CommandObjectTypeSynthAdd::CollectPythonScript (SynthAddOptions *options, - CommandReturnObject &result) -{ - InputReaderSP reader_sp (new TypeSynthAddInputReader(m_interpreter.GetDebugger())); - if (reader_sp && options) - { - - InputReaderEZ::InitializationParameters ipr; - - Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); - } -} - bool CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturnObject &result) { @@ -3842,7 +3787,11 @@ CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturn } } - CollectPythonScript(options,result); + m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } @@ -3921,6 +3870,7 @@ CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd (CommandInterpreter &interp "type synthetic add", "Add a new synthetic provider for a type.", NULL), + IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry type_arg; @@ -3979,32 +3929,17 @@ CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, return false; } - category->GetRegexSyntheticNavigator()->Delete(type_name); - category->GetRegexSyntheticNavigator()->Add(typeRX, entry); + category->GetRegexTypeSyntheticsContainer()->Delete(type_name); + category->GetRegexTypeSyntheticsContainer()->Add(typeRX, entry); return true; } else { - category->GetSyntheticNavigator()->Add(type_name, entry); + category->GetTypeSyntheticsContainer()->Add(type_name, entry); return true; } } - -bool -CommandObjectTypeSynthAdd::DoExecute (Args& command, CommandReturnObject &result) -{ - if (m_options.handwrite_python) - return Execute_HandwritePython(command, result); - else if (m_options.is_class_based) - return Execute_PythonClass(command, result); - else - { - result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); - result.SetStatus(eReturnStatusFailed); - return false; - } -} OptionDefinition CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = @@ -4173,14 +4108,14 @@ private: return false; } - category->GetRegexFilterNavigator()->Delete(type_name); - category->GetRegexFilterNavigator()->Add(typeRX, entry); + category->GetRegexTypeFiltersContainer()->Delete(type_name); + category->GetRegexTypeFiltersContainer()->Add(typeRX, entry); return true; } else { - category->GetFilterNavigator()->Add(type_name, entry); + category->GetTypeFiltersContainer()->Add(type_name, entry); return true; } } @@ -4279,6 +4214,8 @@ protected: Error error; + WarnOnPotentialUnquotedUnsignedType(command, result); + for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); diff --git a/source/Commands/CommandObjectWatchpointCommand.cpp b/source/Commands/CommandObjectWatchpointCommand.cpp index e19216d74fce..0083ff140e5a 100644 --- a/source/Commands/CommandObjectWatchpointCommand.cpp +++ b/source/Commands/CommandObjectWatchpointCommand.cpp @@ -16,6 +16,7 @@ #include "CommandObjectWatchpointCommand.h" #include "CommandObjectWatchpoint.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" @@ -34,7 +35,9 @@ using namespace lldb_private; //------------------------------------------------------------------------- -class CommandObjectWatchpointCommandAdd : public CommandObjectParsed +class CommandObjectWatchpointCommandAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: @@ -43,6 +46,7 @@ public: "add", "Add a set of commands to a watchpoint, to be executed whenever the watchpoint is hit.", NULL), + IOHandlerDelegateMultiline("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong ( @@ -185,40 +189,45 @@ but do NOT enter more than one command per line. \n" ); return &m_options; } - void - CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, - CommandReturnObject &result) + virtual void + IOHandlerActivated (IOHandler &io_handler) { - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - std::unique_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData()); - if (reader_sp && data_ap.get()) + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); - wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); - - Error err (reader_sp->Initialize (CommandObjectWatchpointCommandAdd::GenerateWatchpointCommandCallback, - wp_options, // callback_data - eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token - "> ", // prompt - true)); // echo input - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } + output_sp->PutCString("Enter your debugger command(s). Type 'DONE' to end.\n"); + output_sp->Flush(); } - else + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &line) + { + io_handler.SetIsDone(true); + + // The WatchpointOptions object is owned by the watchpoint or watchpoint location + WatchpointOptions *wp_options = (WatchpointOptions *) io_handler.GetUserData(); + if (wp_options) { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); + std::unique_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData()); + if (data_ap.get()) + { + data_ap->user_source.SplitIntoLines(line); + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); + } } + } + void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result) + { + m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + wp_options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions } /// Set a one-liner as the callback for the watchpoint. @@ -240,93 +249,6 @@ but do NOT enter more than one command per line. \n" ); return; } - - static size_t - GenerateWatchpointCommandCallback (void *callback_data, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_reader_instructions); - if (reader.GetPrompt()) - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderReactivate: - if (reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - if (bytes && bytes_len && callback_data) - { - WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; - if (wp_options) - { - Baton *wp_options_baton = wp_options->GetBaton(); - if (wp_options_baton) - ((WatchpointOptions::CommandData *)wp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); - } - } - if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderInterrupt: - { - // Finish, and cancel the watchpoint command. - reader.SetIsDone (true); - WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; - if (wp_options) - { - Baton *wp_options_baton = wp_options->GetBaton (); - if (wp_options_baton) - { - ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->user_source.Clear(); - ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->script_source.clear(); - } - } - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to watchpoint.\n"); - out_stream->Flush(); - } - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - break; - } - - return bytes_len; - } static bool WatchpointOptionsCallbackFunction (void *baton, @@ -579,12 +501,8 @@ protected: private: CommandOptions m_options; - static const char *g_reader_instructions; - }; -const char * -CommandObjectWatchpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; // FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting // language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper. diff --git a/source/Commands/CommandObjectWatchpointCommand.h b/source/Commands/CommandObjectWatchpointCommand.h index c2faf7187db9..3bc9b3537db7 100644 --- a/source/Commands/CommandObjectWatchpointCommand.h +++ b/source/Commands/CommandObjectWatchpointCommand.h @@ -19,9 +19,6 @@ #include "lldb/lldb-types.h" #include "lldb/Interpreter/Options.h" -#include "lldb/Core/InputReader.h" -#include "lldb/Interpreter/CommandObject.h" -#include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp index de2165cff84e..5ac2bcce70f0 100644 --- a/source/Core/Address.cpp +++ b/source/Core/Address.cpp @@ -16,6 +16,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Symbol/SymbolVendor.h" @@ -327,15 +328,27 @@ Address::GetLoadAddress (Target *target) const addr_t Address::GetCallableLoadAddress (Target *target, bool is_indirect) const { - if (is_indirect && target) { + addr_t code_addr = LLDB_INVALID_ADDRESS; + + if (is_indirect && target) + { ProcessSP processSP = target->GetProcessSP(); Error error; if (processSP.get()) - return processSP->ResolveIndirectFunction(this, error); + { + code_addr = processSP->ResolveIndirectFunction(this, error); + if (!error.Success()) + code_addr = LLDB_INVALID_ADDRESS; + } } - - addr_t code_addr = GetLoadAddress (target); - + else + { + code_addr = GetLoadAddress (target); + } + + if (code_addr == LLDB_INVALID_ADDRESS) + return code_addr; + if (target) return target->GetCallableLoadAddress (code_addr, GetAddressClass()); return code_addr; diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp index f2eb3751a4b5..f4fa22437a9c 100644 --- a/source/Core/ArchSpec.cpp +++ b/source/Core/ArchSpec.cpp @@ -104,6 +104,7 @@ static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486sx , "i486sx" }, { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64 , "x86_64" }, + { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64h , "x86_64h" }, { eByteOrderLittle, 4, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach32 , "unknown-mach-32" }, { eByteOrderLittle, 8, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach64 , "unknown-mach-64" } }; @@ -205,10 +206,11 @@ static const ArchDefinitionEntry g_macho_arch_entries[] = { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , 3 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPU_TYPE_I386 , 4 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPU_TYPE_I386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64h , llvm::MachO::CPU_TYPE_X86_64 , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, // Catch any unknown mach architectures so we can always use the object and symbol mach-o files { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPU_ARCH_ABI64 , 0 , 0xFF000000u, 0x00000000u } @@ -349,14 +351,16 @@ FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) ArchSpec::ArchSpec() : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { } ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { if (triple_cstr) SetTriple(triple_cstr, platform); @@ -366,7 +370,8 @@ ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : ArchSpec::ArchSpec (const char *triple_cstr) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { if (triple_cstr) SetTriple(triple_cstr); @@ -375,7 +380,8 @@ ArchSpec::ArchSpec (const char *triple_cstr) : ArchSpec::ArchSpec(const llvm::Triple &triple) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { SetTriple(triple); } @@ -383,7 +389,8 @@ ArchSpec::ArchSpec(const llvm::Triple &triple) : ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { SetArchitecture (arch_type, cpu, subtype); } @@ -403,6 +410,7 @@ ArchSpec::operator= (const ArchSpec& rhs) m_triple = rhs.m_triple; m_core = rhs.m_core; m_byte_order = rhs.m_byte_order; + m_distribution_id = rhs.m_distribution_id; } return *this; } @@ -413,6 +421,7 @@ ArchSpec::Clear() m_triple = llvm::Triple(); m_core = kCore_invalid; m_byte_order = eByteOrderInvalid; + m_distribution_id.Clear (); } //===----------------------------------------------------------------------===// @@ -468,6 +477,18 @@ ArchSpec::GetMachine () const return llvm::Triple::UnknownArch; } +const ConstString& +ArchSpec::GetDistributionId () const +{ + return m_distribution_id; +} + +void +ArchSpec::SetDistributionId (const char* distribution_id) +{ + m_distribution_id.SetCString (distribution_id); +} + uint32_t ArchSpec::GetAddressByteSize() const { @@ -763,6 +784,8 @@ ArchSpec::IsCompatibleMatch (const ArchSpec& rhs) const bool ArchSpec::IsEqualTo (const ArchSpec& rhs, bool exact_match) const { + // explicitly ignoring m_distribution_id in this method. + if (GetByteOrder() != rhs.GetByteOrder()) return false; @@ -873,7 +896,7 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in if (core2 == ArchSpec::kCore_arm_any) return true; break; - + case ArchSpec::kCore_x86_32_any: if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) return true; diff --git a/source/Core/Broadcaster.cpp b/source/Core/Broadcaster.cpp index 5af7497c8da9..88f39961832f 100644 --- a/source/Core/Broadcaster.cpp +++ b/source/Core/Broadcaster.cpp @@ -313,18 +313,22 @@ Broadcaster::RestoreBroadcaster () { Mutex::Locker event_types_locker(m_listeners_mutex); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); - if (log) + if (!m_hijacking_listeners.empty()) { - Listener *listener = m_hijacking_listeners.back(); - log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", - this, - m_broadcaster_name.AsCString(""), - listener->m_name.c_str(), - listener); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + Listener *listener = m_hijacking_listeners.back(); + log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.pop_back(); } - m_hijacking_listeners.pop_back(); - m_hijacking_masks.pop_back(); + if (!m_hijacking_masks.empty()) + m_hijacking_masks.pop_back(); } ConstString & diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp index 6ea7a11426be..f05ce320b5be 100644 --- a/source/Core/Communication.cpp +++ b/source/Core/Communication.cpp @@ -269,6 +269,16 @@ Communication::StopReadThread (Error *error_ptr) return status; } +bool +Communication::JoinReadThread (Error *error_ptr) +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + bool success = Host::ThreadJoin (m_read_thread, NULL, error_ptr); + m_read_thread = LLDB_INVALID_HOST_THREAD; + return success; +} size_t Communication::GetCachedBytes (void *dst, size_t dst_len) diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Core/ConnectionFileDescriptor.cpp index 5764a212ab43..ed876e52c9af 100644 --- a/source/Core/ConnectionFileDescriptor.cpp +++ b/source/Core/ConnectionFileDescriptor.cpp @@ -97,11 +97,11 @@ ConnectionFileDescriptor::ConnectionFileDescriptor () : m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), m_udp_send_sockaddr (new SocketAddress()), - m_should_close_fd (false), m_socket_timeout_usec(0), m_pipe_read(-1), m_pipe_write(-1), m_mutex (Mutex::eMutexTypeRecursive), + m_should_close_fd (false), m_shutting_down (false) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -116,11 +116,11 @@ ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), m_udp_send_sockaddr (new SocketAddress()), - m_should_close_fd (owns_fd), m_socket_timeout_usec(0), m_pipe_read(-1), m_pipe_write(-1), m_mutex (Mutex::eMutexTypeRecursive), + m_should_close_fd (owns_fd), m_shutting_down (false) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -218,12 +218,15 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) if (s && s[0]) { - char *end = NULL; if (strstr(s, "listen://")) { // listen://HOST:PORT - unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); - return SocketListen (listen_port, error_ptr); + return SocketListen (s + strlen("listen://"), error_ptr); + } + else if (strstr(s, "accept://")) + { + // unix://SOCKNAME + return NamedSocketAccept (s + strlen("accept://"), error_ptr); } else if (strstr(s, "unix-accept://")) { @@ -363,6 +366,9 @@ ConnectionFileDescriptor::Disconnect (Error *error_ptr) if (log) log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", this); + // Reset the port predicate when disconnecting and don't broadcast + m_port_predicate.SetValue(0, eBroadcastNever); + ConnectionStatus status = eConnectionStatusSuccess; if (m_fd_send < 0 && m_fd_recv < 0) @@ -1281,16 +1287,31 @@ ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *er } ConnectionStatus -ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +ConnectionFileDescriptor::SocketListen (const char *host_and_port, Error *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::SocketListen (port = %i)", this, listen_port_num); + log->Printf ("%p ConnectionFileDescriptor::SocketListen (%s)", this, host_and_port); Disconnect (NULL); m_fd_send_type = m_fd_recv_type = eFDTypeSocket; - int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listen_port == -1) + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + { + // Might be just a port number + port = Args::StringToSInt32(host_and_port, -1); + if (port == -1) + return eConnectionStatusError; + else + host_str.clear(); + } + const sa_family_t family = AF_INET; + const int socktype = SOCK_STREAM; + const int protocol = IPPROTO_TCP; + int listen_fd = ::socket (family, socktype, protocol); + if (listen_fd == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); @@ -1298,41 +1319,119 @@ ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_p } // enable local address reuse - SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1); - SocketAddress localhost; - if (localhost.SetToLocalhost (AF_INET, listen_port_num)) + SocketAddress listen_addr; + if (host_str.empty()) + listen_addr.SetToLocalhost(family, port); + else if (host_str.compare("*") == 0) + listen_addr.SetToAnyAddress(family, port); + else { - int err = ::bind (listen_port, localhost, localhost.GetLength()); + if (!listen_addr.getaddrinfo(host_str.c_str(), port_str.c_str(), family, socktype, protocol)) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unable to resolve hostname '%s'", host_str.c_str()); + Close (listen_fd, eFDTypeSocket, NULL); + return eConnectionStatusError; + } + } + + SocketAddress anyaddr; + if (anyaddr.SetToAnyAddress (family, port)) + { + int err = ::bind (listen_fd, anyaddr, anyaddr.GetLength()); if (err == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } - err = ::listen (listen_port, 1); + err = ::listen (listen_fd, 1); if (err == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } - m_fd_send = m_fd_recv = ::accept (listen_port, NULL, 0); + // We were asked to listen on port zero which means we + // must now read the actual port that was given to us + // as port zero is a special code for "find an open port + // for me". + if (port == 0) + port = GetSocketPort(listen_fd); + + // Set the port predicate since when doing a listen://<host>:<port> + // it often needs to accept the incoming connection which is a blocking + // system call. Allowing access to the bound port using a predicate allows + // us to wait for the port predicate to be set to a non-zero value from + // another thread in an efficient manor. + m_port_predicate.SetValue(port, eBroadcastAlways); + + + bool accept_connection = false; + + // Loop until we are happy with our connection + while (!accept_connection) + { + struct sockaddr_in accept_addr; + ::memset (&accept_addr, 0, sizeof accept_addr); +#if !(defined (__linux__) || defined(_MSC_VER)) + accept_addr.sin_len = sizeof accept_addr; +#endif + socklen_t accept_addr_len = sizeof accept_addr; + + int fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len); + + if (fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + break; + } + + if (listen_addr.sockaddr_in().sin_addr.s_addr == INADDR_ANY) + { + accept_connection = true; + m_fd_send = m_fd_recv = fd; + } + else + { + if ( +#if !(defined(__linux__) || (defined(_MSC_VER))) + accept_addr_len == listen_addr.sockaddr_in().sin_len && +#endif + accept_addr.sin_addr.s_addr == listen_addr.sockaddr_in().sin_addr.s_addr) + { + accept_connection = true; + m_fd_send = m_fd_recv = fd; + } + else + { + ::close (fd); + m_fd_send = m_fd_recv = -1; + const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; + const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sockaddr_in().sin_addr.s_addr; + ::fprintf (stderr, "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n", + accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], + listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); + } + } + } + if (m_fd_send == -1) { - if (error_ptr) - error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } } // We are done with the listen port - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); m_should_close_fd = true; @@ -1446,7 +1545,7 @@ ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_pt { // Socket was created, now lets bind to the requested port SocketAddress addr; - addr.SetToLocalhost (AF_INET, 0); + addr.SetToAnyAddress (AF_INET, 0); if (::bind (m_fd_recv, addr, addr.GetLength()) == -1) { @@ -1581,21 +1680,23 @@ ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec) return false; } -in_port_t +uint16_t ConnectionFileDescriptor::GetSocketPort (int fd) { // We bound to port zero, so we need to figure out which port we actually bound to - SocketAddress sock_addr; - socklen_t sock_addr_len = sock_addr.GetMaxLength (); - if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) - return sock_addr.GetPort (); - + if (fd >= 0) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + } return 0; } // If the read file descriptor is a socket, then return // the port number that is being used by the socket. -in_port_t +uint16_t ConnectionFileDescriptor::GetReadPort () const { return ConnectionFileDescriptor::GetSocketPort (m_fd_recv); @@ -1603,10 +1704,23 @@ ConnectionFileDescriptor::GetReadPort () const // If the write file descriptor is a socket, then return // the port number that is being used by the socket. -in_port_t +uint16_t ConnectionFileDescriptor::GetWritePort () const { return ConnectionFileDescriptor::GetSocketPort (m_fd_send); } - +uint16_t +ConnectionFileDescriptor::GetBoundPort (uint32_t timeout_sec) +{ + uint16_t bound_port = 0; + if (timeout_sec == UINT32_MAX) + m_port_predicate.WaitForValueNotEqualTo (0, bound_port); + else + { + TimeValue timeout = TimeValue::Now(); + timeout.OffsetWithSeconds(timeout_sec); + m_port_predicate.WaitForValueNotEqualTo (0, bound_port, &timeout); + } + return bound_port; +} diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp index d1f3c09c2305..b42c6ff31449 100644 --- a/source/Core/DataExtractor.cpp +++ b/source/Core/DataExtractor.cpp @@ -37,6 +37,7 @@ #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" using namespace lldb; @@ -45,69 +46,97 @@ using namespace lldb_private; static inline uint16_t ReadInt16(const unsigned char* ptr, offset_t offset) { - return *(uint16_t *)(ptr + offset); + uint16_t value; + memcpy (&value, ptr + offset, 2); + return value; } + static inline uint32_t ReadInt32 (const unsigned char* ptr, offset_t offset) { - return *(uint32_t *)(ptr + offset); + uint32_t value; + memcpy (&value, ptr + offset, 4); + return value; } static inline uint64_t ReadInt64(const unsigned char* ptr, offset_t offset) { - return *(uint64_t *)(ptr + offset); + uint64_t value; + memcpy (&value, ptr + offset, 8); + return value; } static inline uint16_t ReadInt16(const void* ptr) { - return *(uint16_t *)(ptr); + uint16_t value; + memcpy (&value, ptr, 2); + return value; } + static inline uint32_t ReadInt32 (const void* ptr) { - return *(uint32_t *)(ptr); + uint32_t value; + memcpy (&value, ptr, 4); + return value; } static inline uint64_t ReadInt64(const void* ptr) { - return *(uint64_t *)(ptr); + uint64_t value; + memcpy (&value, ptr, 8); + return value; } static inline uint16_t ReadSwapInt16(const unsigned char* ptr, offset_t offset) { - return llvm::ByteSwap_16(*(uint16_t *)(ptr + offset)); + uint16_t value; + memcpy (&value, ptr + offset, 2); + return llvm::ByteSwap_16(value); } static inline uint32_t ReadSwapInt32 (const unsigned char* ptr, offset_t offset) { - return llvm::ByteSwap_32(*(uint32_t *)(ptr + offset)); + uint32_t value; + memcpy (&value, ptr + offset, 4); + return llvm::ByteSwap_32(value); } + static inline uint64_t ReadSwapInt64(const unsigned char* ptr, offset_t offset) { - return llvm::ByteSwap_64(*(uint64_t *)(ptr + offset)); + uint64_t value; + memcpy (&value, ptr + offset, 8); + return llvm::ByteSwap_64(value); } static inline uint16_t ReadSwapInt16(const void* ptr) { - return llvm::ByteSwap_16(*(uint16_t *)(ptr)); + uint16_t value; + memcpy (&value, ptr, 2); + return llvm::ByteSwap_16(value); } static inline uint32_t ReadSwapInt32 (const void* ptr) { - return llvm::ByteSwap_32(*(uint32_t *)(ptr)); + uint32_t value; + memcpy (&value, ptr, 4); + return llvm::ByteSwap_32(value); } + static inline uint64_t ReadSwapInt64(const void* ptr) { - return llvm::ByteSwap_64(*(uint64_t *)(ptr)); + uint64_t value; + memcpy (&value, ptr, 8); + return llvm::ByteSwap_64(value); } #define NON_PRINTABLE_CHAR '.' @@ -502,13 +531,17 @@ uint32_t DataExtractor::GetU32 (offset_t *offset_ptr) const { uint32_t val = 0; - const uint32_t *data = (const uint32_t *)GetData (offset_ptr, sizeof(val)); + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, sizeof(val)); if (data) { if (m_byte_order != lldb::endian::InlHostByteOrder()) + { val = ReadSwapInt32 (data); + } else - val = *data; + { + memcpy (&val, data, 4); + } } return val; } @@ -561,13 +594,17 @@ uint64_t DataExtractor::GetU64 (offset_t *offset_ptr) const { uint64_t val = 0; - const uint64_t *data = (const uint64_t *)GetData (offset_ptr, sizeof(val)); + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, sizeof(val)); if (data) { if (m_byte_order != lldb::endian::InlHostByteOrder()) + { val = ReadSwapInt64 (data); + } else - val = *data; + { + memcpy (&val, data, 8); + } } return val; } @@ -1808,6 +1845,7 @@ DataExtractor::Dump (Stream *s, case ArchSpec::eCore_x86_32_i486: case ArchSpec::eCore_x86_32_i486sx: case ArchSpec::eCore_x86_64_x86_64: + case ArchSpec::eCore_x86_64_x86_64h: // clang will assert when contructing the apfloat if we use a 16 byte integer value if (GetAPInt (*this, &offset, 10, apint)) { diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp index b57c6051a961..5b346ed636d6 100644 --- a/source/Core/Debugger.cpp +++ b/source/Core/Debugger.cpp @@ -18,13 +18,13 @@ #include "lldb/lldb-private.h" #include "lldb/Core/ConnectionFileDescriptor.h" -#include "lldb/Core/InputReader.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamAsynchronousIO.h" #include "lldb/Core/StreamCallback.h" +#include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/ValueObject.h" @@ -180,6 +180,7 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); if (str.length()) new_prompt = str.c_str(); + GetCommandInterpreter().UpdatePrompt(new_prompt); EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); } @@ -196,12 +197,16 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, StreamString feedback_stream; if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) { - for (auto error : errors) + StreamFileSP stream_sp (GetErrorFile()); + if (stream_sp) { - GetErrorStream().Printf("%s\n",error.AsCString()); + for (auto error : errors) + { + stream_sp->Printf("%s\n",error.AsCString()); + } + if (feedback_stream.GetSize()) + stream_sp->Printf("%s",feedback_stream.GetData()); } - if (feedback_stream.GetSize()) - GetErrorStream().Printf("%s",feedback_stream.GetData()); } } } @@ -246,8 +251,7 @@ Debugger::SetPrompt(const char *p) std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); if (str.length()) new_prompt = str.c_str(); - EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));; - GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); + GetCommandInterpreter().UpdatePrompt(new_prompt); } const char * @@ -611,10 +615,9 @@ Debugger::FindTargetWithProcess (Process *process) Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : UserID (g_unique_id++), Properties(OptionValuePropertiesSP(new OptionValueProperties())), - m_input_comm("debugger.input"), - m_input_file (), - m_output_file (), - m_error_file (), + m_input_file_sp (new StreamFile (stdin, false)), + m_output_file_sp (new StreamFile (stdout, false)), + m_error_file_sp (new StreamFile (stderr, false)), m_terminal_state (), m_target_list (*this), m_platform_list (), @@ -623,8 +626,11 @@ Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : m_source_file_cache(), m_command_interpreter_ap (new CommandInterpreter (*this, eScriptLanguageDefault, false)), m_input_reader_stack (), - m_input_reader_data (), - m_instance_name() + m_instance_name (), + m_loaded_plugins (), + m_event_handler_thread (LLDB_INVALID_HOST_THREAD), + m_io_handler_thread (LLDB_INVALID_HOST_THREAD), + m_event_handler_thread_alive(false) { char instance_cstr[256]; snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID()); @@ -667,7 +673,9 @@ Debugger::~Debugger () void Debugger::Clear() { - CleanUpInputReaders(); + ClearIOHandlers(); + StopIOHandlerThread(); + StopEventHandlerThread(); m_listener.Clear(); int num_targets = m_target_list.GetNumTargets(); for (int i = 0; i < num_targets; i++) @@ -686,23 +694,21 @@ Debugger::Clear() // Close the input file _before_ we close the input read communications class // as it does NOT own the input file, our m_input_file does. m_terminal_state.Clear(); - GetInputFile().Close (); - // Now that we have closed m_input_file, we can now tell our input communication - // class to close down. Its read thread should quickly exit after we close - // the input file handle above. - m_input_comm.Clear (); + if (m_input_file_sp) + m_input_file_sp->GetFile().Close (); } bool Debugger::GetCloseInputOnEOF () const { - return m_input_comm.GetCloseOnEOF(); +// return m_input_comm.GetCloseOnEOF(); + return false; } void Debugger::SetCloseInputOnEOF (bool b) { - m_input_comm.SetCloseOnEOF(b); +// m_input_comm.SetCloseOnEOF(b); } bool @@ -721,37 +727,28 @@ Debugger::SetAsyncExecution (bool async_execution) void Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) { - File &in_file = GetInputFile(); - in_file.SetStream (fh, tranfer_ownership); + if (m_input_file_sp) + m_input_file_sp->GetFile().SetStream (fh, tranfer_ownership); + else + m_input_file_sp.reset (new StreamFile (fh, tranfer_ownership)); + + File &in_file = m_input_file_sp->GetFile(); if (in_file.IsValid() == false) in_file.SetStream (stdin, true); - // Disconnect from any old connection if we had one - m_input_comm.Disconnect (); - // Pass false as the second argument to ConnectionFileDescriptor below because - // our "in_file" above will already take ownership if requested and we don't - // want to objects trying to own and close a file descriptor. - m_input_comm.SetConnection (new ConnectionFileDescriptor (in_file.GetDescriptor(), false)); - m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); - // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState. SaveInputTerminalState (); - - Error error; - if (m_input_comm.StartReadThread (&error) == false) - { - File &err_file = GetErrorFile(); - - err_file.Printf ("error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); - exit(1); - } } void Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) { - File &out_file = GetOutputFile(); - out_file.SetStream (fh, tranfer_ownership); + if (m_output_file_sp) + m_output_file_sp->GetFile().SetStream (fh, tranfer_ownership); + else + m_output_file_sp.reset (new StreamFile (fh, tranfer_ownership)); + + File &out_file = m_output_file_sp->GetFile(); if (out_file.IsValid() == false) out_file.SetStream (stdout, false); @@ -766,8 +763,12 @@ Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) void Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) { - File &err_file = GetErrorFile(); - err_file.SetStream (fh, tranfer_ownership); + if (m_error_file_sp) + m_error_file_sp->GetFile().SetStream (fh, tranfer_ownership); + else + m_error_file_sp.reset (new StreamFile (fh, tranfer_ownership)); + + File &err_file = m_error_file_sp->GetFile(); if (err_file.IsValid() == false) err_file.SetStream (stderr, false); } @@ -775,9 +776,12 @@ Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) void Debugger::SaveInputTerminalState () { - File &in_file = GetInputFile(); - if (in_file.GetDescriptor() != File::kInvalidDescriptor) - m_terminal_state.Save(in_file.GetDescriptor(), true); + if (m_input_file_sp) + { + File &in_file = m_input_file_sp->GetFile(); + if (in_file.GetDescriptor() != File::kInvalidDescriptor) + m_terminal_state.Save(in_file.GetDescriptor(), true); + } } void @@ -812,245 +816,211 @@ Debugger::GetSelectedExecutionContext () return exe_ctx; } -InputReaderSP -Debugger::GetCurrentInputReader () -{ - InputReaderSP reader_sp; - - if (!m_input_reader_stack.IsEmpty()) - { - // Clear any finished readers from the stack - while (CheckIfTopInputReaderIsDone()) ; - - if (!m_input_reader_stack.IsEmpty()) - reader_sp = m_input_reader_stack.Top(); - } - - return reader_sp; -} - -void -Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) -{ - if (bytes_len > 0) - ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); - else - ((Debugger *)baton)->DispatchInputEndOfFile (); -} - - -void -Debugger::DispatchInput (const char *bytes, size_t bytes_len) -{ - if (bytes == NULL || bytes_len == 0) - return; - - WriteToDefaultReader (bytes, bytes_len); -} - void Debugger::DispatchInputInterrupt () { - m_input_reader_data.clear(); - - InputReaderSP reader_sp (GetCurrentInputReader ()); + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) - { - reader_sp->Notify (eInputReaderInterrupt); - - // If notifying the reader of the interrupt finished the reader, we should pop it off the stack. - while (CheckIfTopInputReaderIsDone ()) ; - } + reader_sp->Interrupt(); } void Debugger::DispatchInputEndOfFile () { - m_input_reader_data.clear(); - - InputReaderSP reader_sp (GetCurrentInputReader ()); + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) - { - reader_sp->Notify (eInputReaderEndOfFile); - - // If notifying the reader of the end-of-file finished the reader, we should pop it off the stack. - while (CheckIfTopInputReaderIsDone ()) ; - } + reader_sp->GotEOF(); } void -Debugger::CleanUpInputReaders () +Debugger::ClearIOHandlers () { - m_input_reader_data.clear(); - // The bottom input reader should be the main debugger input reader. We do not want to close that one here. + Mutex::Locker locker (m_input_reader_stack.GetMutex()); while (m_input_reader_stack.GetSize() > 1) { - InputReaderSP reader_sp (GetCurrentInputReader ()); + IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) { - reader_sp->Notify (eInputReaderEndOfFile); - reader_sp->SetIsDone (true); + m_input_reader_stack.Pop(); + reader_sp->SetIsDone(true); + reader_sp->Interrupt(); } } } void -Debugger::NotifyTopInputReader (InputReaderAction notification) +Debugger::ExecuteIOHanders() { - InputReaderSP reader_sp (GetCurrentInputReader()); - if (reader_sp) - { - reader_sp->Notify (notification); + + while (1) + { + IOHandlerSP reader_sp(m_input_reader_stack.Top()); + if (!reader_sp) + break; - // Flush out any input readers that are done. - while (CheckIfTopInputReaderIsDone ()) - /* Do nothing. */; + reader_sp->Activate(); + reader_sp->Run(); + reader_sp->Deactivate(); + + // Remove all input readers that are done from the top of the stack + while (1) + { + IOHandlerSP top_reader_sp = m_input_reader_stack.Top(); + if (top_reader_sp && top_reader_sp->GetIsDone()) + m_input_reader_stack.Pop(); + else + break; + } } + ClearIOHandlers(); } bool -Debugger::InputReaderIsTopReader (const InputReaderSP& reader_sp) +Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp) { - InputReaderSP top_reader_sp (GetCurrentInputReader()); + return m_input_reader_stack.IsTop (reader_sp); +} + - return (reader_sp.get() == top_reader_sp.get()); +ConstString +Debugger::GetTopIOHandlerControlSequence(char ch) +{ + return m_input_reader_stack.GetTopIOHandlerControlSequence (ch); } - void -Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) +Debugger::RunIOHandler (const IOHandlerSP& reader_sp) { - if (bytes && bytes_len) - m_input_reader_data.append (bytes, bytes_len); - - if (m_input_reader_data.empty()) - return; + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + PushIOHandler (reader_sp); + reader_sp->Activate(); + reader_sp->Run(); + PopIOHandler (reader_sp); +} - while (!m_input_reader_stack.IsEmpty() && !m_input_reader_data.empty()) +void +Debugger::AdoptTopIOHandlerFilesIfInvalid (StreamFileSP &in, StreamFileSP &out, StreamFileSP &err) +{ + // Before an IOHandler runs, it must have in/out/err streams. + // This function is called when one ore more of the streams + // are NULL. We use the top input reader's in/out/err streams, + // or fall back to the debugger file handles, or we fall back + // onto stdin/stdout/stderr as a last resort. + + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + IOHandlerSP top_reader_sp (m_input_reader_stack.Top()); + // If no STDIN has been set, then set it appropriately + if (!in) { - // Get the input reader from the top of the stack - InputReaderSP reader_sp (GetCurrentInputReader ()); - if (!reader_sp) - break; - - size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.c_str(), - m_input_reader_data.size()); - if (bytes_handled) - { - m_input_reader_data.erase (0, bytes_handled); - } + if (top_reader_sp) + in = top_reader_sp->GetInputStreamFile(); else - { - // No bytes were handled, we might not have reached our - // granularity, just return and wait for more data - break; - } + in = GetInputFile(); + + // If there is nothing, use stdin + if (!in) + in = StreamFileSP(new StreamFile(stdin, false)); + } + // If no STDOUT has been set, then set it appropriately + if (!out) + { + if (top_reader_sp) + out = top_reader_sp->GetOutputStreamFile(); + else + out = GetOutputFile(); + + // If there is nothing, use stdout + if (!out) + out = StreamFileSP(new StreamFile(stdout, false)); + } + // If no STDERR has been set, then set it appropriately + if (!err) + { + if (top_reader_sp) + err = top_reader_sp->GetErrorStreamFile(); + else + err = GetErrorFile(); + + // If there is nothing, use stderr + if (!err) + err = StreamFileSP(new StreamFile(stdout, false)); + } - - // Flush out any input readers that are done. - while (CheckIfTopInputReaderIsDone ()) - /* Do nothing. */; - } void -Debugger::PushInputReader (const InputReaderSP& reader_sp) +Debugger::PushIOHandler (const IOHandlerSP& reader_sp) { if (!reader_sp) return; - // Deactivate the old top reader - InputReaderSP top_reader_sp (GetCurrentInputReader ()); + // Got the current top input reader... + IOHandlerSP top_reader_sp (m_input_reader_stack.Top()); - if (top_reader_sp) - top_reader_sp->Notify (eInputReaderDeactivate); - + // Push our new input reader m_input_reader_stack.Push (reader_sp); - reader_sp->Notify (eInputReaderActivate); - ActivateInputReader (reader_sp); + + // Interrupt the top input reader to it will exit its Run() function + // and let this new input reader take over + if (top_reader_sp) + top_reader_sp->Deactivate(); } bool -Debugger::PopInputReader (const InputReaderSP& pop_reader_sp) +Debugger::PopIOHandler (const IOHandlerSP& pop_reader_sp) { bool result = false; + + Mutex::Locker locker (m_input_reader_stack.GetMutex()); // The reader on the stop of the stack is done, so let the next // read on the stack referesh its prompt and if there is one... if (!m_input_reader_stack.IsEmpty()) { - // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. - InputReaderSP reader_sp(m_input_reader_stack.Top()); + IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) { + reader_sp->Deactivate(); m_input_reader_stack.Pop (); - reader_sp->Notify (eInputReaderDeactivate); - reader_sp->Notify (eInputReaderDone); - result = true; + + reader_sp = m_input_reader_stack.Top(); + if (reader_sp) + reader_sp->Activate(); - if (!m_input_reader_stack.IsEmpty()) - { - reader_sp = m_input_reader_stack.Top(); - if (reader_sp) - { - ActivateInputReader (reader_sp); - reader_sp->Notify (eInputReaderReactivate); - } - } + result = true; } } return result; } bool -Debugger::CheckIfTopInputReaderIsDone () +Debugger::HideTopIOHandler() { - bool result = false; - if (!m_input_reader_stack.IsEmpty()) + Mutex::Locker locker; + + if (locker.TryLock(m_input_reader_stack.GetMutex())) { - // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. - InputReaderSP reader_sp(m_input_reader_stack.Top()); - - if (reader_sp && reader_sp->IsDone()) - { - result = true; - PopInputReader (reader_sp); - } + IOHandlerSP reader_sp(m_input_reader_stack.Top()); + if (reader_sp) + reader_sp->Hide(); + return true; } - return result; + return false; } void -Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +Debugger::RefreshTopIOHandler() { - int input_fd = m_input_file.GetFile().GetDescriptor(); - - if (input_fd >= 0) - { - Terminal tty(input_fd); - - tty.SetEcho(reader_sp->GetEcho()); - - switch (reader_sp->GetGranularity()) - { - case eInputReaderGranularityByte: - case eInputReaderGranularityWord: - tty.SetCanonical (false); - break; - - case eInputReaderGranularityLine: - case eInputReaderGranularityAll: - tty.SetCanonical (true); - break; - - default: - break; - } - } + IOHandlerSP reader_sp(m_input_reader_stack.Top()); + if (reader_sp) + reader_sp->Refresh(); } + StreamSP Debugger::GetAsyncOutputStream () { @@ -2624,7 +2594,7 @@ Debugger::EnableLog (const char *channel, const char **categories, const char *l } else if (log_file == NULL || *log_file == '\0') { - log_stream_sp.reset(new StreamFile(GetOutputFile().GetDescriptor(), false)); + log_stream_sp = GetOutputFile(); } else { @@ -2680,3 +2650,514 @@ Debugger::GetSourceManager () } + +// This function handles events that were broadcast by the process. +void +Debugger::HandleBreakpointEvent (const EventSP &event_sp) +{ + using namespace lldb; + const uint32_t event_type = Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (event_sp); + +// if (event_type & eBreakpointEventTypeAdded +// || event_type & eBreakpointEventTypeRemoved +// || event_type & eBreakpointEventTypeEnabled +// || event_type & eBreakpointEventTypeDisabled +// || event_type & eBreakpointEventTypeCommandChanged +// || event_type & eBreakpointEventTypeConditionChanged +// || event_type & eBreakpointEventTypeIgnoreChanged +// || event_type & eBreakpointEventTypeLocationsResolved) +// { +// // Don't do anything about these events, since the breakpoint commands already echo these actions. +// } +// + if (event_type & eBreakpointEventTypeLocationsAdded) + { + uint32_t num_new_locations = Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(event_sp); + if (num_new_locations > 0) + { + BreakpointSP breakpoint = Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp); + StreamFileSP output_sp (GetOutputFile()); + if (output_sp) + { + output_sp->Printf("%d location%s added to breakpoint %d\n", + num_new_locations, + num_new_locations == 1 ? "" : "s", + breakpoint->GetID()); + RefreshTopIOHandler(); + } + } + } +// else if (event_type & eBreakpointEventTypeLocationsRemoved) +// { +// // These locations just get disabled, not sure it is worth spamming folks about this on the command line. +// } +// else if (event_type & eBreakpointEventTypeLocationsResolved) +// { +// // This might be an interesting thing to note, but I'm going to leave it quiet for now, it just looked noisy. +// } +} + +size_t +Debugger::GetProcessSTDOUT (Process *process, Stream *stream) +{ + size_t total_bytes = 0; + if (stream == NULL) + stream = GetOutputFile().get(); + + if (stream) + { + // The process has stuff waiting for stdout; get it and write it out to the appropriate place. + if (process == NULL) + { + TargetSP target_sp = GetTargetList().GetSelectedTarget(); + if (target_sp) + process = target_sp->GetProcessSP().get(); + } + if (process) + { + Error error; + size_t len; + char stdio_buffer[1024]; + while ((len = process->GetSTDOUT (stdio_buffer, sizeof (stdio_buffer), error)) > 0) + { + stream->Write(stdio_buffer, len); + total_bytes += len; + } + } + stream->Flush(); + } + return total_bytes; +} + +size_t +Debugger::GetProcessSTDERR (Process *process, Stream *stream) +{ + size_t total_bytes = 0; + if (stream == NULL) + stream = GetOutputFile().get(); + + if (stream) + { + // The process has stuff waiting for stderr; get it and write it out to the appropriate place. + if (process == NULL) + { + TargetSP target_sp = GetTargetList().GetSelectedTarget(); + if (target_sp) + process = target_sp->GetProcessSP().get(); + } + if (process) + { + Error error; + size_t len; + char stdio_buffer[1024]; + while ((len = process->GetSTDERR (stdio_buffer, sizeof (stdio_buffer), error)) > 0) + { + stream->Write(stdio_buffer, len); + total_bytes += len; + } + } + stream->Flush(); + } + return total_bytes; +} + +// This function handles events that were broadcast by the process. +void +Debugger::HandleProcessEvent (const EventSP &event_sp) +{ + using namespace lldb; + const uint32_t event_type = event_sp->GetType(); + ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + const bool gui_enabled = IsForwardingEvents(); + bool top_io_handler_hid = false; + if (gui_enabled == false) + top_io_handler_hid = HideTopIOHandler(); + + assert (process_sp); + + if (event_type & Process::eBroadcastBitSTDOUT) + { + // The process has stdout available, get it and write it out to the + // appropriate place. + if (top_io_handler_hid) + GetProcessSTDOUT (process_sp.get(), NULL); + } + else if (event_type & Process::eBroadcastBitSTDERR) + { + // The process has stderr available, get it and write it out to the + // appropriate place. + if (top_io_handler_hid) + GetProcessSTDERR (process_sp.get(), NULL); + } + else if (event_type & Process::eBroadcastBitStateChanged) + { + // Drain all stout and stderr so we don't see any output come after + // we print our prompts + if (top_io_handler_hid) + { + StreamFileSP stream_sp (GetOutputFile()); + GetProcessSTDOUT (process_sp.get(), stream_sp.get()); + GetProcessSTDERR (process_sp.get(), NULL); + // Something changed in the process; get the event and report the process's current status and location to + // the user. + StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + if (event_state == eStateInvalid) + return; + + switch (event_state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + { + stream_sp->Printf("Process %" PRIu64 " %s\n", + process_sp->GetID(), + StateAsCString (event_state)); + } + break; + + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + process_sp->GetStatus(*stream_sp); + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) + { + size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); + if (num_reasons > 0) + { + // FIXME: Do we want to report this, or would that just be annoyingly chatty? + if (num_reasons == 1) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); + stream_sp->Printf("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : "<UNKNOWN REASON>"); + } + else + { + stream_sp->Printf("Process %" PRIu64 " stopped and restarted, reasons:\n", + process_sp->GetID()); + + + for (size_t i = 0; i < num_reasons; i++) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); + stream_sp->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>"); + } + } + } + } + else + { + // Lock the thread list so it doesn't change on us + ThreadList &thread_list = process_sp->GetThreadList(); + Mutex::Locker locker (thread_list.GetMutex()); + + ThreadSP curr_thread (thread_list.GetSelectedThread()); + ThreadSP thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + if (curr_thread) + curr_thread_stop_reason = curr_thread->GetStopReason(); + if (!curr_thread || + !curr_thread->IsValid() || + curr_thread_stop_reason == eStopReasonInvalid || + curr_thread_stop_reason == eStopReasonNone) + { + // Prefer a thread that has just completed its plan over another thread as current thread. + ThreadSP plan_thread; + ThreadSP other_thread; + const size_t num_threads = thread_list.GetSize(); + size_t i; + for (i = 0; i < num_threads; ++i) + { + thread = thread_list.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread->GetStopReason(); + switch (thread_stop_reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + if (!other_thread) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread) + plan_thread = thread; + break; + } + } + if (plan_thread) + thread_list.SetSelectedThreadByID (plan_thread->GetID()); + else if (other_thread) + thread_list.SetSelectedThreadByID (other_thread->GetID()); + else + { + if (curr_thread && curr_thread->IsValid()) + thread = curr_thread; + else + thread = thread_list.GetThreadAtIndex(0); + + if (thread) + thread_list.SetSelectedThreadByID (thread->GetID()); + } + } + + if (GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) + { + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + process_sp->GetStatus(*stream_sp); + process_sp->GetThreadStatus (*stream_sp, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + } + else + { + uint32_t target_idx = GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream_sp->Printf ("Target %d: (", target_idx); + else + stream_sp->Printf ("Target <unknown index>: ("); + process_sp->GetTarget().Dump (stream_sp.get(), eDescriptionLevelBrief); + stream_sp->Printf (") stopped.\n"); + } + } + break; + } + } + } + + if (top_io_handler_hid) + RefreshTopIOHandler(); +} + +void +Debugger::HandleThreadEvent (const EventSP &event_sp) +{ + // At present the only thread event we handle is the Frame Changed event, + // and all we do for that is just reprint the thread status for that thread. + using namespace lldb; + const uint32_t event_type = event_sp->GetType(); + if (event_type == Thread::eBroadcastBitStackChanged || + event_type == Thread::eBroadcastBitThreadSelected ) + { + ThreadSP thread_sp (Thread::ThreadEventData::GetThreadFromEvent (event_sp.get())); + if (thread_sp) + { + HideTopIOHandler(); + StreamFileSP stream_sp (GetOutputFile()); + thread_sp->GetStatus(*stream_sp, 0, 1, 1); + RefreshTopIOHandler(); + } + } +} + +bool +Debugger::IsForwardingEvents () +{ + return (bool)m_forward_listener_sp; +} + +void +Debugger::EnableForwardEvents (const ListenerSP &listener_sp) +{ + m_forward_listener_sp = listener_sp; +} + +void +Debugger::CancelForwardEvents (const ListenerSP &listener_sp) +{ + m_forward_listener_sp.reset(); +} + + +void +Debugger::DefaultEventHandler() +{ + Listener& listener(GetListener()); + ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); + ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); + ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); + BroadcastEventSpec target_event_spec (broadcaster_class_target, + Target::eBroadcastBitBreakpointChanged); + + BroadcastEventSpec process_event_spec (broadcaster_class_process, + Process::eBroadcastBitStateChanged | + Process::eBroadcastBitSTDOUT | + Process::eBroadcastBitSTDERR); + + BroadcastEventSpec thread_event_spec (broadcaster_class_thread, + Thread::eBroadcastBitStackChanged | + Thread::eBroadcastBitThreadSelected ); + + listener.StartListeningForEventSpec (*this, target_event_spec); + listener.StartListeningForEventSpec (*this, process_event_spec); + listener.StartListeningForEventSpec (*this, thread_event_spec); + listener.StartListeningForEvents (m_command_interpreter_ap.get(), + CommandInterpreter::eBroadcastBitQuitCommandReceived | + CommandInterpreter::eBroadcastBitAsynchronousOutputData | + CommandInterpreter::eBroadcastBitAsynchronousErrorData ); + + bool done = false; + while (!done) + { +// Mutex::Locker locker; +// if (locker.TryLock(m_input_reader_stack.GetMutex())) +// { +// if (m_input_reader_stack.IsEmpty()) +// break; +// } +// + EventSP event_sp; + if (listener.WaitForEvent(NULL, event_sp)) + { + if (event_sp) + { + Broadcaster *broadcaster = event_sp->GetBroadcaster(); + if (broadcaster) + { + uint32_t event_type = event_sp->GetType(); + ConstString broadcaster_class (broadcaster->GetBroadcasterClass()); + if (broadcaster_class == broadcaster_class_process) + { + HandleProcessEvent (event_sp); + } + else if (broadcaster_class == broadcaster_class_target) + { + if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(event_sp.get())) + { + HandleBreakpointEvent (event_sp); + } + } + else if (broadcaster_class == broadcaster_class_thread) + { + HandleThreadEvent (event_sp); + } + else if (broadcaster == m_command_interpreter_ap.get()) + { + if (event_type & CommandInterpreter::eBroadcastBitQuitCommandReceived) + { + done = true; + } + else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousErrorData) + { + const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); + if (data && data[0]) + { + StreamFileSP error_sp (GetErrorFile()); + if (error_sp) + { + HideTopIOHandler(); + error_sp->PutCString(data); + error_sp->Flush(); + RefreshTopIOHandler(); + } + } + } + else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousOutputData) + { + const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); + if (data && data[0]) + { + StreamFileSP output_sp (GetOutputFile()); + if (output_sp) + { + HideTopIOHandler(); + output_sp->PutCString(data); + output_sp->Flush(); + RefreshTopIOHandler(); + } + } + } + } + } + + if (m_forward_listener_sp) + m_forward_listener_sp->AddEvent(event_sp); + } + } + } +} + +lldb::thread_result_t +Debugger::EventHandlerThread (lldb::thread_arg_t arg) +{ + ((Debugger *)arg)->DefaultEventHandler(); + return NULL; +} + +bool +Debugger::StartEventHandlerThread() +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread)) + m_event_handler_thread = Host::ThreadCreate("lldb.debugger.event-handler", EventHandlerThread, this, NULL); + return IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread); +} + +void +Debugger::StopEventHandlerThread() +{ + if (IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread)) + { + GetCommandInterpreter().BroadcastEvent(CommandInterpreter::eBroadcastBitQuitCommandReceived); + Host::ThreadJoin(m_event_handler_thread, NULL, NULL); + m_event_handler_thread = LLDB_INVALID_HOST_THREAD; + } +} + + +lldb::thread_result_t +Debugger::IOHandlerThread (lldb::thread_arg_t arg) +{ + Debugger *debugger = (Debugger *)arg; + debugger->ExecuteIOHanders(); + debugger->StopEventHandlerThread(); + return NULL; +} + +bool +Debugger::StartIOHandlerThread() +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread)) + m_io_handler_thread = Host::ThreadCreate("lldb.debugger.io-handler", IOHandlerThread, this, NULL); + return IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread); +} + +void +Debugger::StopIOHandlerThread() +{ + if (IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread)) + { + if (m_input_file_sp) + m_input_file_sp->GetFile().Close(); + Host::ThreadJoin(m_io_handler_thread, NULL, NULL); + m_io_handler_thread = LLDB_INVALID_HOST_THREAD; + } +} + + diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp index 7f830acba1f7..1d2b8cf04c32 100644 --- a/source/Core/Disassembler.cpp +++ b/source/Core/Disassembler.cpp @@ -35,6 +35,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -1044,10 +1045,8 @@ InstructionList::GetIndexOfNextBranchInstruction(uint32_t start) const } uint32_t -InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +InstructionList::GetIndexOfInstructionAtAddress (const Address &address) { - Address address; - address.SetLoadAddress(load_addr, &target); size_t num_instructions = m_instructions.size(); uint32_t index = UINT32_MAX; for (size_t i = 0; i < num_instructions; i++) @@ -1061,6 +1060,15 @@ InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Tar return index; } + +uint32_t +InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +{ + Address address; + address.SetLoadAddress(load_addr, &target); + return GetIndexOfInstructionAtAddress(address); +} + size_t Disassembler::ParseInstructions (const ExecutionContext *exe_ctx, const AddressRange &range, @@ -1235,25 +1243,25 @@ PseudoInstruction::SetOpcode (size_t opcode_size, void *opcode_data) case 8: { uint8_t value8 = *((uint8_t *) opcode_data); - m_opcode.SetOpcode8 (value8); + m_opcode.SetOpcode8 (value8, eByteOrderInvalid); break; } case 16: { uint16_t value16 = *((uint16_t *) opcode_data); - m_opcode.SetOpcode16 (value16); + m_opcode.SetOpcode16 (value16, eByteOrderInvalid); break; } case 32: { uint32_t value32 = *((uint32_t *) opcode_data); - m_opcode.SetOpcode32 (value32); + m_opcode.SetOpcode32 (value32, eByteOrderInvalid); break; } case 64: { uint64_t value64 = *((uint64_t *) opcode_data); - m_opcode.SetOpcode64 (value64); + m_opcode.SetOpcode64 (value64, eByteOrderInvalid); break; } default: diff --git a/source/Core/DynamicLoader.cpp b/source/Core/DynamicLoader.cpp index 82f84048b32a..1f545b727a1e 100644 --- a/source/Core/DynamicLoader.cpp +++ b/source/Core/DynamicLoader.cpp @@ -10,7 +10,11 @@ #include "lldb/lldb-private.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" using namespace lldb; using namespace lldb_private; @@ -74,3 +78,137 @@ DynamicLoader::SetStopWhenImagesChange (bool stop) m_process->SetStopOnSharedLibraryEvents (stop); } +ModuleSP +DynamicLoader::GetTargetExecutable() +{ + Target &target = m_process->GetTarget(); + ModuleSP executable = target.GetExecutableModule(); + + if (executable.get()) + { + if (executable->GetFileSpec().Exists()) + { + ModuleSpec module_spec (executable->GetFileSpec(), executable->GetArchitecture()); + ModuleSP module_sp (new Module (module_spec)); + + // Check if the executable has changed and set it to the target executable if they differ. + if (module_sp.get() && module_sp->GetUUID().IsValid() && executable->GetUUID().IsValid()) + { + if (module_sp->GetUUID() != executable->GetUUID()) + executable.reset(); + } + else if (executable->FileHasChanged()) + { + executable.reset(); + } + + if (!executable.get()) + { + executable = target.GetSharedModule(module_spec); + if (executable.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded + const bool get_dependent_images = false; + target.SetExecutableModule(executable, get_dependent_images); + } + } + } + } + return executable; +} + +void +DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, addr_t base_addr) +{ + UpdateLoadedSectionsCommon(module, base_addr); +} + +void +DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module, addr_t base_addr) +{ + bool changed; + const bool base_addr_is_offset = true; + module->SetLoadAddress(m_process->GetTarget(), base_addr, base_addr_is_offset, changed); +} + +void +DynamicLoader::UnloadSections(const ModuleSP module) +{ + UnloadSectionsCommon(module); +} + +void +DynamicLoader::UnloadSectionsCommon(const ModuleSP module) +{ + Target &target = m_process->GetTarget(); + const SectionList *sections = GetSectionListFromModule(module); + + assert(sections && "SectionList missing from unloaded module."); + + const size_t num_sections = sections->GetSize(); + for (size_t i = 0; i < num_sections; ++i) + { + SectionSP section_sp (sections->GetSectionAtIndex(i)); + target.SetSectionUnloaded(section_sp); + } +} + + +const SectionList * +DynamicLoader::GetSectionListFromModule(const ModuleSP module) const +{ + SectionList *sections = nullptr; + if (module.get()) + { + ObjectFile *obj_file = module->GetObjectFile(); + if (obj_file) + { + sections = obj_file->GetSectionList(); + } + } + return sections; +} + +ModuleSP +DynamicLoader::LoadModuleAtAddress(const FileSpec &file, addr_t link_map_addr, addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + ModuleSpec module_spec (file, target.GetArchitecture()); + if ((module_sp = modules.FindFirstModule (module_spec))) + { + UpdateLoadedSections(module_sp, link_map_addr, base_addr); + } + else if ((module_sp = target.GetSharedModule(module_spec))) + { + UpdateLoadedSections(module_sp, link_map_addr, base_addr); + } + + return module_sp; +} + +int64_t +DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr, int size_in_bytes) +{ + Error error; + + uint64_t value = m_process->ReadUnsignedIntegerFromMemory(addr, size_in_bytes, 0, error); + if (error.Fail()) + return -1; + else + return (int64_t)value; +} + +addr_t +DynamicLoader::ReadPointer(addr_t addr) +{ + Error error; + addr_t value = m_process->ReadPointerFromMemory(addr, error); + if (error.Fail()) + return LLDB_INVALID_ADDRESS; + else + return value; +} diff --git a/source/Core/IOHandler.cpp b/source/Core/IOHandler.cpp new file mode 100644 index 000000000000..bdec19ccb06f --- /dev/null +++ b/source/Core/IOHandler.cpp @@ -0,0 +1,5294 @@ +//===-- IOHandler.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/lldb-python.h" + +#include <string> + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/IOHandler.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Host/Editline.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/ThreadPlan.h" + +#ifndef LLDB_DISABLE_CURSES +#include <ncurses.h> +#include <panel.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +IOHandler::IOHandler (Debugger &debugger) : + IOHandler (debugger, + StreamFileSP(), // Adopt STDIN from top input reader + StreamFileSP(), // Adopt STDOUT from top input reader + StreamFileSP(), // Adopt STDERR from top input reader + 0) // Flags +{ +} + + +IOHandler::IOHandler (Debugger &debugger, + const lldb::StreamFileSP &input_sp, + const lldb::StreamFileSP &output_sp, + const lldb::StreamFileSP &error_sp, + uint32_t flags) : + m_debugger (debugger), + m_input_sp (input_sp), + m_output_sp (output_sp), + m_error_sp (error_sp), + m_flags (flags), + m_user_data (NULL), + m_done (false), + m_active (false) +{ + // If any files are not specified, then adopt them from the top input reader. + if (!m_input_sp || !m_output_sp || !m_error_sp) + debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp, + m_output_sp, + m_error_sp); +} + +IOHandler::~IOHandler() +{ +} + + +int +IOHandler::GetInputFD() +{ + if (m_input_sp) + return m_input_sp->GetFile().GetDescriptor(); + return -1; +} + +int +IOHandler::GetOutputFD() +{ + if (m_output_sp) + return m_output_sp->GetFile().GetDescriptor(); + return -1; +} + +int +IOHandler::GetErrorFD() +{ + if (m_error_sp) + return m_error_sp->GetFile().GetDescriptor(); + return -1; +} + +FILE * +IOHandler::GetInputFILE() +{ + if (m_input_sp) + return m_input_sp->GetFile().GetStream(); + return NULL; +} + +FILE * +IOHandler::GetOutputFILE() +{ + if (m_output_sp) + return m_output_sp->GetFile().GetStream(); + return NULL; +} + +FILE * +IOHandler::GetErrorFILE() +{ + if (m_error_sp) + return m_error_sp->GetFile().GetStream(); + return NULL; +} + +StreamFileSP & +IOHandler::GetInputStreamFile() +{ + return m_input_sp; +} + +StreamFileSP & +IOHandler::GetOutputStreamFile() +{ + return m_output_sp; +} + + +StreamFileSP & +IOHandler::GetErrorStreamFile() +{ + return m_error_sp; +} + +bool +IOHandler::GetIsInteractive () +{ + return GetInputStreamFile()->GetFile().GetIsInteractive (); +} + +bool +IOHandler::GetIsRealTerminal () +{ + return GetInputStreamFile()->GetFile().GetIsRealTerminal(); +} + +IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger, + const char *prompt, + bool default_response) : + IOHandlerEditline(debugger, + NULL, // NULL editline_name means no history loaded/saved + NULL, + false, // Multi-line + *this), + m_default_response (default_response), + m_user_response (default_response) +{ + StreamString prompt_stream; + prompt_stream.PutCString(prompt); + if (m_default_response) + prompt_stream.Printf(": [Y/n] "); + else + prompt_stream.Printf(": [y/N] "); + + SetPrompt (prompt_stream.GetString().c_str()); + +} + + +IOHandlerConfirm::~IOHandlerConfirm () +{ +} + +int +IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler, + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches) +{ + if (current_line == cursor) + { + if (m_default_response) + { + matches.AppendString("y"); + } + else + { + matches.AppendString("n"); + } + } + return matches.GetSize(); +} + +void +IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line) +{ + if (line.empty()) + { + // User just hit enter, set the response to the default + m_user_response = m_default_response; + io_handler.SetIsDone(true); + return; + } + + if (line.size() == 1) + { + switch (line[0]) + { + case 'y': + case 'Y': + m_user_response = true; + io_handler.SetIsDone(true); + return; + case 'n': + case 'N': + m_user_response = false; + io_handler.SetIsDone(true); + return; + default: + break; + } + } + + if (line == "yes" || line == "YES" || line == "Yes") + { + m_user_response = true; + io_handler.SetIsDone(true); + } + else if (line == "no" || line == "NO" || line == "No") + { + m_user_response = false; + io_handler.SetIsDone(true); + } +} + +int +IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler, + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches) +{ + switch (m_completion) + { + case Completion::None: + break; + + case Completion::LLDBCommand: + return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line, + cursor, + last_char, + skip_first_n_matches, + max_matches, + matches); + + case Completion::Expression: + { + bool word_complete = false; + const char *word_start = cursor; + if (cursor > current_line) + --word_start; + while (word_start > current_line && !isspace(*word_start)) + --word_start; + CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(), + CommandCompletions::eVariablePathCompletion, + word_start, + skip_first_n_matches, + max_matches, + NULL, + word_complete, + matches); + + size_t num_matches = matches.GetSize(); + if (num_matches > 0) + { + std::string common_prefix; + matches.LongestCommonPrefix (common_prefix); + const size_t partial_name_len = strlen(word_start); + + // If we matched a unique single command, add a space... + // Only do this if the completer told us this was a complete word, however... + if (num_matches == 1 && word_complete) + { + common_prefix.push_back(' '); + } + common_prefix.erase (0, partial_name_len); + matches.InsertStringAtIndex(0, std::move(common_prefix)); + } + return num_matches; + } + break; + } + + + return 0; +} + + +IOHandlerEditline::IOHandlerEditline (Debugger &debugger, + const char *editline_name, // Used for saving history files + const char *prompt, + bool multi_line, + IOHandlerDelegate &delegate) : + IOHandlerEditline(debugger, + StreamFileSP(), // Inherit input from top input reader + StreamFileSP(), // Inherit output from top input reader + StreamFileSP(), // Inherit error from top input reader + 0, // Flags + editline_name, // Used for saving history files + prompt, + multi_line, + delegate) +{ +} + +IOHandlerEditline::IOHandlerEditline (Debugger &debugger, + const lldb::StreamFileSP &input_sp, + const lldb::StreamFileSP &output_sp, + const lldb::StreamFileSP &error_sp, + uint32_t flags, + const char *editline_name, // Used for saving history files + const char *prompt, + bool multi_line, + IOHandlerDelegate &delegate) : + IOHandler (debugger, input_sp, output_sp, error_sp, flags), + m_editline_ap (), + m_delegate (delegate), + m_prompt (), + m_multi_line (multi_line) +{ + SetPrompt(prompt); + + bool use_editline = false; + +#ifndef _MSC_VER + use_editline = m_input_sp->GetFile().GetIsRealTerminal(); +#else + use_editline = true; +#endif + + if (use_editline) + { + m_editline_ap.reset(new Editline (editline_name, + prompt ? prompt : "", + GetInputFILE (), + GetOutputFILE (), + GetErrorFILE ())); + m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this); + m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this); + } + +} + +IOHandlerEditline::~IOHandlerEditline () +{ + m_editline_ap.reset(); +} + + +bool +IOHandlerEditline::GetLine (std::string &line) +{ + if (m_editline_ap) + { + return m_editline_ap->GetLine(line).Success(); + } + else + { + line.clear(); + + FILE *in = GetInputFILE(); + if (in) + { + if (GetIsInteractive()) + { + const char *prompt = GetPrompt(); + if (prompt && prompt[0]) + { + FILE *out = GetOutputFILE(); + if (out) + { + ::fprintf(out, "%s", prompt); + ::fflush(out); + } + } + } + char buffer[256]; + bool done = false; + bool got_line = false; + while (!done) + { + if (fgets(buffer, sizeof(buffer), in) == NULL) + done = true; + else + { + got_line = true; + size_t buffer_len = strlen(buffer); + assert (buffer[buffer_len] == '\0'); + char last_char = buffer[buffer_len-1]; + if (last_char == '\r' || last_char == '\n') + { + done = true; + // Strip trailing newlines + while (last_char == '\r' || last_char == '\n') + { + --buffer_len; + if (buffer_len == 0) + break; + last_char = buffer[buffer_len-1]; + } + } + line.append(buffer, buffer_len); + } + } + // We might have gotten a newline on a line by itself + // make sure to return true in this case. + return got_line; + } + else + { + // No more input file, we are done... + SetIsDone(true); + } + return false; + } +} + + +LineStatus +IOHandlerEditline::LineCompletedCallback (Editline *editline, + StringList &lines, + uint32_t line_idx, + Error &error, + void *baton) +{ + IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; + return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error); +} + +int +IOHandlerEditline::AutoCompleteCallback (const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches, + void *baton) +{ + IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; + if (editline_reader) + return editline_reader->m_delegate.IOHandlerComplete (*editline_reader, + current_line, + cursor, + last_char, + skip_first_n_matches, + max_matches, + matches); + return 0; +} + +const char * +IOHandlerEditline::GetPrompt () +{ + if (m_editline_ap) + return m_editline_ap->GetPrompt (); + else if (m_prompt.empty()) + return NULL; + return m_prompt.c_str(); +} + +bool +IOHandlerEditline::SetPrompt (const char *p) +{ + if (p && p[0]) + m_prompt = p; + else + m_prompt.clear(); + if (m_editline_ap) + m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str()); + return true; +} + +bool +IOHandlerEditline::GetLines (StringList &lines) +{ + bool success = false; + if (m_editline_ap) + { + std::string end_token; + success = m_editline_ap->GetLines(end_token, lines).Success(); + } + else + { + LineStatus lines_status = LineStatus::Success; + + while (lines_status == LineStatus::Success) + { + std::string line; + if (GetLine(line)) + { + lines.AppendString(line); + Error error; + lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error); + } + else + { + lines_status = LineStatus::Done; + } + } + success = lines.GetSize() > 0; + } + return success; +} + +// Each IOHandler gets to run until it is done. It should read data +// from the "in" and place output into "out" and "err and return +// when done. +void +IOHandlerEditline::Run () +{ + std::string line; + while (IsActive()) + { + if (m_multi_line) + { + StringList lines; + if (GetLines (lines)) + { + line = lines.CopyList(); + m_delegate.IOHandlerInputComplete(*this, line); + } + else + { + m_done = true; + } + } + else + { + if (GetLine(line)) + { + m_delegate.IOHandlerInputComplete(*this, line); + } + else + { + m_done = true; + } + } + } +} + +void +IOHandlerEditline::Hide () +{ + if (m_editline_ap && m_editline_ap->GettingLine()) + m_editline_ap->Hide(); +} + + +void +IOHandlerEditline::Refresh () +{ + if (m_editline_ap && m_editline_ap->GettingLine()) + m_editline_ap->Refresh(); + else + { + const char *prompt = GetPrompt(); + if (prompt && prompt[0]) + { + FILE *out = GetOutputFILE(); + if (out) + { + ::fprintf(out, "%s", prompt); + ::fflush(out); + } + } + } +} + +void +IOHandlerEditline::Interrupt () +{ + if (m_editline_ap) + m_editline_ap->Interrupt(); +} + +void +IOHandlerEditline::GotEOF() +{ + if (m_editline_ap) + m_editline_ap->Interrupt(); +} + +// we may want curses to be disabled for some builds +// for instance, windows +#ifndef LLDB_DISABLE_CURSES + +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/StackFrame.h" + +#define KEY_RETURN 10 +#define KEY_ESCAPE 27 + +namespace curses +{ + class Menu; + class MenuDelegate; + class Window; + class WindowDelegate; + typedef std::shared_ptr<Menu> MenuSP; + typedef std::shared_ptr<MenuDelegate> MenuDelegateSP; + typedef std::shared_ptr<Window> WindowSP; + typedef std::shared_ptr<WindowDelegate> WindowDelegateSP; + typedef std::vector<MenuSP> Menus; + typedef std::vector<WindowSP> Windows; + typedef std::vector<WindowDelegateSP> WindowDelegates; + +#if 0 +type summary add -s "x=${var.x}, y=${var.y}" curses::Point +type summary add -s "w=${var.width}, h=${var.height}" curses::Size +type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect +#endif + struct Point + { + int x; + int y; + + Point (int _x = 0, int _y = 0) : + x(_x), + y(_y) + { + } + + void + Clear () + { + x = 0; + y = 0; + } + + Point & + operator += (const Point &rhs) + { + x += rhs.x; + y += rhs.y; + return *this; + } + + void + Dump () + { + printf ("(x=%i, y=%i)\n", x, y); + } + + }; + + bool operator == (const Point &lhs, const Point &rhs) + { + return lhs.x == rhs.x && lhs.y == rhs.y; + } + bool operator != (const Point &lhs, const Point &rhs) + { + return lhs.x != rhs.x || lhs.y != rhs.y; + } + + struct Size + { + int width; + int height; + Size (int w = 0, int h = 0) : + width (w), + height (h) + { + } + + void + Clear () + { + width = 0; + height = 0; + } + + void + Dump () + { + printf ("(w=%i, h=%i)\n", width, height); + } + + }; + + bool operator == (const Size &lhs, const Size &rhs) + { + return lhs.width == rhs.width && lhs.height == rhs.height; + } + bool operator != (const Size &lhs, const Size &rhs) + { + return lhs.width != rhs.width || lhs.height != rhs.height; + } + + struct Rect + { + Point origin; + Size size; + + Rect () : + origin(), + size() + { + } + + Rect (const Point &p, const Size &s) : + origin (p), + size (s) + { + } + + void + Clear () + { + origin.Clear(); + size.Clear(); + } + + void + Dump () + { + printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height); + } + + void + Inset (int w, int h) + { + if (size.width > w*2) + size.width -= w*2; + origin.x += w; + + if (size.height > h*2) + size.height -= h*2; + origin.y += h; + } + // Return a status bar rectangle which is the last line of + // this rectangle. This rectangle will be modified to not + // include the status bar area. + Rect + MakeStatusBar () + { + Rect status_bar; + if (size.height > 1) + { + status_bar.origin.x = origin.x; + status_bar.origin.y = size.height; + status_bar.size.width = size.width; + status_bar.size.height = 1; + --size.height; + } + return status_bar; + } + + // Return a menubar rectangle which is the first line of + // this rectangle. This rectangle will be modified to not + // include the menubar area. + Rect + MakeMenuBar () + { + Rect menubar; + if (size.height > 1) + { + menubar.origin.x = origin.x; + menubar.origin.y = origin.y; + menubar.size.width = size.width; + menubar.size.height = 1; + ++origin.y; + --size.height; + } + return menubar; + } + + void + HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const + { + float top_height = top_percentage * size.height; + HorizontalSplit (top_height, top, bottom); + } + + void + HorizontalSplit (int top_height, Rect &top, Rect &bottom) const + { + top = *this; + if (top_height < size.height) + { + top.size.height = top_height; + bottom.origin.x = origin.x; + bottom.origin.y = origin.y + top.size.height; + bottom.size.width = size.width; + bottom.size.height = size.height - top.size.height; + } + else + { + bottom.Clear(); + } + } + + void + VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const + { + float left_width = left_percentage * size.width; + VerticalSplit (left_width, left, right); + } + + + void + VerticalSplit (int left_width, Rect &left, Rect &right) const + { + left = *this; + if (left_width < size.width) + { + left.size.width = left_width; + right.origin.x = origin.x + left.size.width; + right.origin.y = origin.y; + right.size.width = size.width - left.size.width; + right.size.height = size.height; + } + else + { + right.Clear(); + } + } + }; + + bool operator == (const Rect &lhs, const Rect &rhs) + { + return lhs.origin == rhs.origin && lhs.size == rhs.size; + } + bool operator != (const Rect &lhs, const Rect &rhs) + { + return lhs.origin != rhs.origin || lhs.size != rhs.size; + } + + enum HandleCharResult + { + eKeyNotHandled = 0, + eKeyHandled = 1, + eQuitApplication = 2 + }; + + enum class MenuActionResult + { + Handled, + NotHandled, + Quit // Exit all menus and quit + }; + + struct KeyHelp + { + int ch; + const char *description; + }; + + class WindowDelegate + { + public: + virtual + ~WindowDelegate() + { + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + return false; // Drawing not handled + } + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int key) + { + return eKeyNotHandled; + } + + virtual const char * + WindowDelegateGetHelpText () + { + return NULL; + } + + virtual KeyHelp * + WindowDelegateGetKeyHelp () + { + return NULL; + } + }; + + class HelpDialogDelegate : + public WindowDelegate + { + public: + HelpDialogDelegate (const char *text, KeyHelp *key_help_array); + + virtual + ~HelpDialogDelegate(); + + virtual bool + WindowDelegateDraw (Window &window, bool force); + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int key); + + size_t + GetNumLines() const + { + return m_text.GetSize(); + } + + size_t + GetMaxLineLength () const + { + return m_text.GetMaxStringLength(); + } + + protected: + StringList m_text; + int m_first_visible_line; + }; + + + class Window + { + public: + + Window (const char *name) : + m_name (name), + m_window (NULL), + m_panel (NULL), + m_parent (NULL), + m_subwindows (), + m_delegate_sp (), + m_curr_active_window_idx (UINT32_MAX), + m_prev_active_window_idx (UINT32_MAX), + m_delete (false), + m_needs_update (true), + m_can_activate (true), + m_is_subwin (false) + { + } + + Window (const char *name, WINDOW *w, bool del = true) : + m_name (name), + m_window (NULL), + m_panel (NULL), + m_parent (NULL), + m_subwindows (), + m_delegate_sp (), + m_curr_active_window_idx (UINT32_MAX), + m_prev_active_window_idx (UINT32_MAX), + m_delete (del), + m_needs_update (true), + m_can_activate (true), + m_is_subwin (false) + { + if (w) + Reset(w); + } + + Window (const char *name, const Rect &bounds) : + m_name (name), + m_window (NULL), + m_parent (NULL), + m_subwindows (), + m_delegate_sp (), + m_curr_active_window_idx (UINT32_MAX), + m_prev_active_window_idx (UINT32_MAX), + m_delete (true), + m_needs_update (true), + m_can_activate (true), + m_is_subwin (false) + { + Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y)); + } + + virtual + ~Window () + { + RemoveSubWindows (); + Reset (); + } + + void + Reset (WINDOW *w = NULL, bool del = true) + { + if (m_window == w) + return; + + if (m_panel) + { + ::del_panel (m_panel); + m_panel = NULL; + } + if (m_window && m_delete) + { + ::delwin (m_window); + m_window = NULL; + m_delete = false; + } + if (w) + { + m_window = w; + m_panel = ::new_panel (m_window); + m_delete = del; + } + } + + void AttributeOn (attr_t attr) { ::wattron (m_window, attr); } + void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); } + void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); } + void Clear () { ::wclear (m_window); } + void Erase () { ::werase (m_window); } + Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window + int GetChar () { return ::wgetch (m_window); } + int GetCursorX () { return getcurx (m_window); } + int GetCursorY () { return getcury (m_window); } + Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system + Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); } + Size GetSize() { return Size (GetWidth(), GetHeight()); } + int GetParentX () { return getparx (m_window); } + int GetParentY () { return getpary (m_window); } + int GetMaxX() { return getmaxx (m_window); } + int GetMaxY() { return getmaxy (m_window); } + int GetWidth() { return GetMaxX(); } + int GetHeight() { return GetMaxY(); } + void MoveCursor (int x, int y) { ::wmove (m_window, y, x); } + void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); } + void Resize (int w, int h) { ::wresize(m_window, h, w); } + void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); } + void PutChar (int ch) { ::waddch (m_window, ch); } + void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); } + void Refresh () { ::wrefresh (m_window); } + void DeferredRefresh () + { + // We are using panels, so we don't need to call this... + //::wnoutrefresh(m_window); + } + void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); } + void UnderlineOn () { AttributeOn(A_UNDERLINE); } + void UnderlineOff () { AttributeOff(A_UNDERLINE); } + + void PutCStringTruncated (const char *s, int right_pad) + { + int bytes_left = GetWidth() - GetCursorX(); + if (bytes_left > right_pad) + { + bytes_left -= right_pad; + ::waddnstr (m_window, s, bytes_left); + } + } + + void + MoveWindow (const Point &origin) + { + const bool moving_window = origin != GetParentOrigin(); + if (m_is_subwin && moving_window) + { + // Can't move subwindows, must delete and re-create + Size size = GetSize(); + Reset (::subwin (m_parent->m_window, + size.height, + size.width, + origin.y, + origin.x), true); + } + else + { + ::mvwin (m_window, origin.y, origin.x); + } + } + + void + SetBounds (const Rect &bounds) + { + const bool moving_window = bounds.origin != GetParentOrigin(); + if (m_is_subwin && moving_window) + { + // Can't move subwindows, must delete and re-create + Reset (::subwin (m_parent->m_window, + bounds.size.height, + bounds.size.width, + bounds.origin.y, + bounds.origin.x), true); + } + else + { + if (moving_window) + MoveWindow(bounds.origin); + Resize (bounds.size); + } + } + + void + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))) + { + va_list args; + va_start (args, format); + vwprintw(m_window, format, args); + va_end (args); + } + + void + Touch () + { + ::touchwin (m_window); + if (m_parent) + m_parent->Touch(); + } + + WindowSP + CreateSubWindow (const char *name, const Rect &bounds, bool make_active) + { + WindowSP subwindow_sp; + if (m_window) + { + subwindow_sp.reset(new Window(name, ::subwin (m_window, + bounds.size.height, + bounds.size.width, + bounds.origin.y, + bounds.origin.x), true)); + subwindow_sp->m_is_subwin = true; + } + else + { + subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height, + bounds.size.width, + bounds.origin.y, + bounds.origin.x), true)); + subwindow_sp->m_is_subwin = false; + } + subwindow_sp->m_parent = this; + if (make_active) + { + m_prev_active_window_idx = m_curr_active_window_idx; + m_curr_active_window_idx = m_subwindows.size(); + } + m_subwindows.push_back(subwindow_sp); + ::top_panel (subwindow_sp->m_panel); + m_needs_update = true; + return subwindow_sp; + } + + bool + RemoveSubWindow (Window *window) + { + Windows::iterator pos, end = m_subwindows.end(); + size_t i = 0; + for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) + { + if ((*pos).get() == window) + { + if (m_prev_active_window_idx == i) + m_prev_active_window_idx = UINT32_MAX; + else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i) + --m_prev_active_window_idx; + + if (m_curr_active_window_idx == i) + m_curr_active_window_idx = UINT32_MAX; + else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i) + --m_curr_active_window_idx; + window->Erase(); + m_subwindows.erase(pos); + m_needs_update = true; + if (m_parent) + m_parent->Touch(); + else + ::touchwin (stdscr); + return true; + } + } + return false; + } + + WindowSP + FindSubWindow (const char *name) + { + Windows::iterator pos, end = m_subwindows.end(); + size_t i = 0; + for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) + { + if ((*pos)->m_name.compare(name) == 0) + return *pos; + } + return WindowSP(); + } + + void + RemoveSubWindows () + { + m_curr_active_window_idx = UINT32_MAX; + m_prev_active_window_idx = UINT32_MAX; + for (Windows::iterator pos = m_subwindows.begin(); + pos != m_subwindows.end(); + pos = m_subwindows.erase(pos)) + { + (*pos)->Erase(); + } + if (m_parent) + m_parent->Touch(); + else + ::touchwin (stdscr); + } + + WINDOW * + get() + { + return m_window; + } + + operator WINDOW *() + { + return m_window; + } + + //---------------------------------------------------------------------- + // Window drawing utilities + //---------------------------------------------------------------------- + void + DrawTitleBox (const char *title, const char *bottom_message = NULL) + { + attr_t attr = 0; + if (IsActive()) + attr = A_BOLD | COLOR_PAIR(2); + else + attr = 0; + if (attr) + AttributeOn(attr); + + Box(); + MoveCursor(3, 0); + + if (title && title[0]) + { + PutChar ('<'); + PutCString (title); + PutChar ('>'); + } + + if (bottom_message && bottom_message[0]) + { + int bottom_message_length = strlen(bottom_message); + int x = GetWidth() - 3 - (bottom_message_length + 2); + + if (x > 0) + { + MoveCursor (x, GetHeight() - 1); + PutChar ('['); + PutCString(bottom_message); + PutChar (']'); + } + else + { + MoveCursor (1, GetHeight() - 1); + PutChar ('['); + PutCStringTruncated (bottom_message, 1); + } + } + if (attr) + AttributeOff(attr); + + } + + virtual void + Draw (bool force) + { + if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force)) + return; + + for (auto &subwindow_sp : m_subwindows) + subwindow_sp->Draw(force); + } + + bool + CreateHelpSubwindow () + { + if (m_delegate_sp) + { + const char *text = m_delegate_sp->WindowDelegateGetHelpText (); + KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp (); + if ((text && text[0]) || key_help) + { + std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help)); + const size_t num_lines = help_delegate_ap->GetNumLines(); + const size_t max_length = help_delegate_ap->GetMaxLineLength(); + Rect bounds = GetBounds(); + bounds.Inset(1, 1); + if (max_length + 4 < bounds.size.width) + { + bounds.origin.x += (bounds.size.width - max_length + 4)/2; + bounds.size.width = max_length + 4; + } + else + { + if (bounds.size.width > 100) + { + const int inset_w = bounds.size.width / 4; + bounds.origin.x += inset_w; + bounds.size.width -= 2*inset_w; + } + } + + if (num_lines + 2 < bounds.size.height) + { + bounds.origin.y += (bounds.size.height - num_lines + 2)/2; + bounds.size.height = num_lines + 2; + } + else + { + if (bounds.size.height > 100) + { + const int inset_h = bounds.size.height / 4; + bounds.origin.y += inset_h; + bounds.size.height -= 2*inset_h; + } + } + WindowSP help_window_sp; + Window *parent_window = GetParent(); + if (parent_window) + help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); + else + help_window_sp = CreateSubWindow("Help", bounds, true); + help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release())); + return true; + } + } + return false; + } + + virtual HandleCharResult + HandleChar (int key) + { + // Always check the active window first + HandleCharResult result = eKeyNotHandled; + WindowSP active_window_sp = GetActiveWindow (); + if (active_window_sp) + { + result = active_window_sp->HandleChar (key); + if (result != eKeyNotHandled) + return result; + } + + if (m_delegate_sp) + { + result = m_delegate_sp->WindowDelegateHandleChar (*this, key); + if (result != eKeyNotHandled) + return result; + } + + // Then check for any windows that want any keys + // that weren't handled. This is typically only + // for a menubar. + // Make a copy of the subwindows in case any HandleChar() + // functions muck with the subwindows. If we don't do this, + // we can crash when iterating over the subwindows. + Windows subwindows (m_subwindows); + for (auto subwindow_sp : subwindows) + { + if (subwindow_sp->m_can_activate == false) + { + HandleCharResult result = subwindow_sp->HandleChar(key); + if (result != eKeyNotHandled) + return result; + } + } + + return eKeyNotHandled; + } + + bool + SetActiveWindow (Window *window) + { + const size_t num_subwindows = m_subwindows.size(); + for (size_t i=0; i<num_subwindows; ++i) + { + if (m_subwindows[i].get() == window) + { + m_prev_active_window_idx = m_curr_active_window_idx; + ::top_panel (window->m_panel); + m_curr_active_window_idx = i; + return true; + } + } + return false; + } + + WindowSP + GetActiveWindow () + { + if (!m_subwindows.empty()) + { + if (m_curr_active_window_idx >= m_subwindows.size()) + { + if (m_prev_active_window_idx < m_subwindows.size()) + { + m_curr_active_window_idx = m_prev_active_window_idx; + m_prev_active_window_idx = UINT32_MAX; + } + else if (IsActive()) + { + m_prev_active_window_idx = UINT32_MAX; + m_curr_active_window_idx = UINT32_MAX; + + // Find first window that wants to be active if this window is active + const size_t num_subwindows = m_subwindows.size(); + for (size_t i=0; i<num_subwindows; ++i) + { + if (m_subwindows[i]->GetCanBeActive()) + { + m_curr_active_window_idx = i; + break; + } + } + } + } + + if (m_curr_active_window_idx < m_subwindows.size()) + return m_subwindows[m_curr_active_window_idx]; + } + return WindowSP(); + } + + bool + GetCanBeActive () const + { + return m_can_activate; + } + + void + SetCanBeActive (bool b) + { + m_can_activate = b; + } + + const WindowDelegateSP & + GetDelegate () const + { + return m_delegate_sp; + } + + void + SetDelegate (const WindowDelegateSP &delegate_sp) + { + m_delegate_sp = delegate_sp; + } + + Window * + GetParent () const + { + return m_parent; + } + + bool + IsActive () const + { + if (m_parent) + return m_parent->GetActiveWindow().get() == this; + else + return true; // Top level window is always active + } + + void + SelectNextWindowAsActive () + { + // Move active focus to next window + const size_t num_subwindows = m_subwindows.size(); + if (m_curr_active_window_idx == UINT32_MAX) + { + uint32_t idx = 0; + for (auto subwindow_sp : m_subwindows) + { + if (subwindow_sp->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + break; + } + ++idx; + } + } + else if (m_curr_active_window_idx + 1 < num_subwindows) + { + bool handled = false; + m_prev_active_window_idx = m_curr_active_window_idx; + for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx) + { + if (m_subwindows[idx]->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + handled = true; + break; + } + } + if (!handled) + { + for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx) + { + if (m_subwindows[idx]->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + break; + } + } + } + } + else + { + m_prev_active_window_idx = m_curr_active_window_idx; + for (size_t idx=0; idx<num_subwindows; ++idx) + { + if (m_subwindows[idx]->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + break; + } + } + } + } + + const char * + GetName () const + { + return m_name.c_str(); + } + protected: + std::string m_name; + WINDOW *m_window; + PANEL *m_panel; + Window *m_parent; + Windows m_subwindows; + WindowDelegateSP m_delegate_sp; + uint32_t m_curr_active_window_idx; + uint32_t m_prev_active_window_idx; + bool m_delete; + bool m_needs_update; + bool m_can_activate; + bool m_is_subwin; + + private: + DISALLOW_COPY_AND_ASSIGN(Window); + }; + + class MenuDelegate + { + public: + virtual ~MenuDelegate() {} + + virtual MenuActionResult + MenuDelegateAction (Menu &menu) = 0; + }; + + class Menu : public WindowDelegate + { + public: + enum class Type + { + Invalid, + Bar, + Item, + Separator + }; + + // Menubar or separator constructor + Menu (Type type); + + // Menuitem constructor + Menu (const char *name, + const char *key_name, + int key_value, + uint64_t identifier); + + virtual ~ + Menu () + { + } + + const MenuDelegateSP & + GetDelegate () const + { + return m_delegate_sp; + } + + void + SetDelegate (const MenuDelegateSP &delegate_sp) + { + m_delegate_sp = delegate_sp; + } + + void + RecalculateNameLengths(); + + void + AddSubmenu (const MenuSP &menu_sp); + + int + DrawAndRunMenu (Window &window); + + void + DrawMenuTitle (Window &window, bool highlight); + + virtual bool + WindowDelegateDraw (Window &window, bool force); + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int key); + + MenuActionResult + ActionPrivate (Menu &menu) + { + MenuActionResult result = MenuActionResult::NotHandled; + if (m_delegate_sp) + { + result = m_delegate_sp->MenuDelegateAction (menu); + if (result != MenuActionResult::NotHandled) + return result; + } + else if (m_parent) + { + result = m_parent->ActionPrivate(menu); + if (result != MenuActionResult::NotHandled) + return result; + } + return m_canned_result; + } + + MenuActionResult + Action () + { + // Call the recursive action so it can try to handle it + // with the menu delegate, and if not, try our parent menu + return ActionPrivate (*this); + } + + void + SetCannedResult (MenuActionResult result) + { + m_canned_result = result; + } + + Menus & + GetSubmenus() + { + return m_submenus; + } + + const Menus & + GetSubmenus() const + { + return m_submenus; + } + + int + GetSelectedSubmenuIndex () const + { + return m_selected; + } + + void + SetSelectedSubmenuIndex (int idx) + { + m_selected = idx; + } + + Type + GetType () const + { + return m_type; + } + + int + GetStartingColumn() const + { + return m_start_col; + } + + void + SetStartingColumn(int col) + { + m_start_col = col; + } + + int + GetKeyValue() const + { + return m_key_value; + } + + void + SetKeyValue(int key_value) + { + m_key_value = key_value; + } + + std::string & + GetName() + { + return m_name; + } + + std::string & + GetKeyName() + { + return m_key_name; + } + + int + GetDrawWidth () const + { + return m_max_submenu_name_length + m_max_submenu_key_name_length + 8; + } + + + uint64_t + GetIdentifier() const + { + return m_identifier; + } + + void + SetIdentifier (uint64_t identifier) + { + m_identifier = identifier; + } + + protected: + std::string m_name; + std::string m_key_name; + uint64_t m_identifier; + Type m_type; + int m_key_value; + int m_start_col; + int m_max_submenu_name_length; + int m_max_submenu_key_name_length; + int m_selected; + Menu *m_parent; + Menus m_submenus; + WindowSP m_menu_window_sp; + MenuActionResult m_canned_result; + MenuDelegateSP m_delegate_sp; + }; + + // Menubar or separator constructor + Menu::Menu (Type type) : + m_name (), + m_key_name (), + m_identifier (0), + m_type (type), + m_key_value (0), + m_start_col (0), + m_max_submenu_name_length (0), + m_max_submenu_key_name_length (0), + m_selected (0), + m_parent (NULL), + m_submenus (), + m_canned_result (MenuActionResult::NotHandled), + m_delegate_sp() + { + } + + // Menuitem constructor + Menu::Menu (const char *name, + const char *key_name, + int key_value, + uint64_t identifier) : + m_name (), + m_key_name (), + m_identifier (identifier), + m_type (Type::Invalid), + m_key_value (key_value), + m_start_col (0), + m_max_submenu_name_length (0), + m_max_submenu_key_name_length (0), + m_selected (0), + m_parent (NULL), + m_submenus (), + m_canned_result (MenuActionResult::NotHandled), + m_delegate_sp() + { + if (name && name[0]) + { + m_name = name; + m_type = Type::Item; + if (key_name && key_name[0]) + m_key_name = key_name; + } + else + { + m_type = Type::Separator; + } + } + + void + Menu::RecalculateNameLengths() + { + m_max_submenu_name_length = 0; + m_max_submenu_key_name_length = 0; + Menus &submenus = GetSubmenus(); + const size_t num_submenus = submenus.size(); + for (size_t i=0; i<num_submenus; ++i) + { + Menu *submenu = submenus[i].get(); + if (m_max_submenu_name_length < submenu->m_name.size()) + m_max_submenu_name_length = submenu->m_name.size(); + if (m_max_submenu_key_name_length < submenu->m_key_name.size()) + m_max_submenu_key_name_length = submenu->m_key_name.size(); + } + } + + void + Menu::AddSubmenu (const MenuSP &menu_sp) + { + menu_sp->m_parent = this; + if (m_max_submenu_name_length < menu_sp->m_name.size()) + m_max_submenu_name_length = menu_sp->m_name.size(); + if (m_max_submenu_key_name_length < menu_sp->m_key_name.size()) + m_max_submenu_key_name_length = menu_sp->m_key_name.size(); + m_submenus.push_back(menu_sp); + } + + void + Menu::DrawMenuTitle (Window &window, bool highlight) + { + if (m_type == Type::Separator) + { + window.MoveCursor(0, window.GetCursorY()); + window.PutChar(ACS_LTEE); + int width = window.GetWidth(); + if (width > 2) + { + width -= 2; + for (size_t i=0; i< width; ++i) + window.PutChar(ACS_HLINE); + } + window.PutChar(ACS_RTEE); + } + else + { + const int shortcut_key = m_key_value; + bool underlined_shortcut = false; + const attr_t hilgight_attr = A_REVERSE; + if (highlight) + window.AttributeOn(hilgight_attr); + if (isprint(shortcut_key)) + { + size_t lower_pos = m_name.find(tolower(shortcut_key)); + size_t upper_pos = m_name.find(toupper(shortcut_key)); + const char *name = m_name.c_str(); + size_t pos = std::min<size_t>(lower_pos, upper_pos); + if (pos != std::string::npos) + { + underlined_shortcut = true; + if (pos > 0) + { + window.PutCString(name, pos); + name += pos; + } + const attr_t shortcut_attr = A_UNDERLINE|A_BOLD; + window.AttributeOn (shortcut_attr); + window.PutChar(name[0]); + window.AttributeOff(shortcut_attr); + name++; + if (name[0]) + window.PutCString(name); + } + } + + if (!underlined_shortcut) + { + window.PutCString(m_name.c_str()); + } + + if (highlight) + window.AttributeOff(hilgight_attr); + + if (m_key_name.empty()) + { + if (!underlined_shortcut && isprint(m_key_value)) + { + window.AttributeOn (COLOR_PAIR(3)); + window.Printf (" (%c)", m_key_value); + window.AttributeOff (COLOR_PAIR(3)); + } + } + else + { + window.AttributeOn (COLOR_PAIR(3)); + window.Printf (" (%s)", m_key_name.c_str()); + window.AttributeOff (COLOR_PAIR(3)); + } + } + } + + bool + Menu::WindowDelegateDraw (Window &window, bool force) + { + Menus &submenus = GetSubmenus(); + const size_t num_submenus = submenus.size(); + const int selected_idx = GetSelectedSubmenuIndex(); + Menu::Type menu_type = GetType (); + switch (menu_type) + { + case Menu::Type::Bar: + { + window.SetBackground(2); + window.MoveCursor(0, 0); + for (size_t i=0; i<num_submenus; ++i) + { + Menu *menu = submenus[i].get(); + if (i > 0) + window.PutChar(' '); + menu->SetStartingColumn (window.GetCursorX()); + window.PutCString("| "); + menu->DrawMenuTitle (window, false); + } + window.PutCString(" |"); + window.DeferredRefresh(); + } + break; + + case Menu::Type::Item: + { + int y = 1; + int x = 3; + // Draw the menu + int cursor_x = 0; + int cursor_y = 0; + window.Erase(); + window.SetBackground(2); + window.Box(); + for (size_t i=0; i<num_submenus; ++i) + { + const bool is_selected = i == selected_idx; + window.MoveCursor(x, y + i); + if (is_selected) + { + // Remember where we want the cursor to be + cursor_x = x-1; + cursor_y = y+i; + } + submenus[i]->DrawMenuTitle (window, is_selected); + } + window.MoveCursor(cursor_x, cursor_y); + window.DeferredRefresh(); + } + break; + + default: + case Menu::Type::Separator: + break; + } + return true; // Drawing handled... + } + + HandleCharResult + Menu::WindowDelegateHandleChar (Window &window, int key) + { + HandleCharResult result = eKeyNotHandled; + + Menus &submenus = GetSubmenus(); + const size_t num_submenus = submenus.size(); + const int selected_idx = GetSelectedSubmenuIndex(); + Menu::Type menu_type = GetType (); + if (menu_type == Menu::Type::Bar) + { + MenuSP run_menu_sp; + switch (key) + { + case KEY_DOWN: + case KEY_UP: + // Show last menu or first menu + if (selected_idx < num_submenus) + run_menu_sp = submenus[selected_idx]; + else if (!submenus.empty()) + run_menu_sp = submenus.front(); + result = eKeyHandled; + break; + + case KEY_RIGHT: + { + ++m_selected; + if (m_selected >= num_submenus) + m_selected = 0; + if (m_selected < num_submenus) + run_menu_sp = submenus[m_selected]; + else if (!submenus.empty()) + run_menu_sp = submenus.front(); + result = eKeyHandled; + } + break; + + case KEY_LEFT: + { + --m_selected; + if (m_selected < 0) + m_selected = num_submenus - 1; + if (m_selected < num_submenus) + run_menu_sp = submenus[m_selected]; + else if (!submenus.empty()) + run_menu_sp = submenus.front(); + result = eKeyHandled; + } + break; + + default: + for (size_t i=0; i<num_submenus; ++i) + { + if (submenus[i]->GetKeyValue() == key) + { + SetSelectedSubmenuIndex(i); + run_menu_sp = submenus[i]; + result = eKeyHandled; + break; + } + } + break; + } + + if (run_menu_sp) + { + // Run the action on this menu in case we need to populate the + // menu with dynamic content and also in case check marks, and + // any other menu decorations need to be caclulated + if (run_menu_sp->Action() == MenuActionResult::Quit) + return eQuitApplication; + + Rect menu_bounds; + menu_bounds.origin.x = run_menu_sp->GetStartingColumn(); + menu_bounds.origin.y = 1; + menu_bounds.size.width = run_menu_sp->GetDrawWidth(); + menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2; + if (m_menu_window_sp) + window.GetParent()->RemoveSubWindow(m_menu_window_sp.get()); + + m_menu_window_sp = window.GetParent()->CreateSubWindow (run_menu_sp->GetName().c_str(), + menu_bounds, + true); + m_menu_window_sp->SetDelegate (run_menu_sp); + } + } + else if (menu_type == Menu::Type::Item) + { + switch (key) + { + case KEY_DOWN: + if (m_submenus.size() > 1) + { + const int start_select = m_selected; + while (++m_selected != start_select) + { + if (m_selected >= num_submenus) + m_selected = 0; + if (m_submenus[m_selected]->GetType() == Type::Separator) + continue; + else + break; + } + return eKeyHandled; + } + break; + + case KEY_UP: + if (m_submenus.size() > 1) + { + const int start_select = m_selected; + while (--m_selected != start_select) + { + if (m_selected < 0) + m_selected = num_submenus - 1; + if (m_submenus[m_selected]->GetType() == Type::Separator) + continue; + else + break; + } + return eKeyHandled; + } + break; + + case KEY_RETURN: + if (selected_idx < num_submenus) + { + if (submenus[selected_idx]->Action() == MenuActionResult::Quit) + return eQuitApplication; + window.GetParent()->RemoveSubWindow(&window); + return eKeyHandled; + } + break; + + case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in case other chars are entered for escaped sequences + window.GetParent()->RemoveSubWindow(&window); + return eKeyHandled; + + default: + { + bool handled = false; + for (size_t i=0; i<num_submenus; ++i) + { + Menu *menu = submenus[i].get(); + if (menu->GetKeyValue() == key) + { + handled = true; + SetSelectedSubmenuIndex(i); + window.GetParent()->RemoveSubWindow(&window); + if (menu->Action() == MenuActionResult::Quit) + return eQuitApplication; + return eKeyHandled; + } + } + } + break; + + } + } + else if (menu_type == Menu::Type::Separator) + { + + } + return result; + } + + + class Application + { + public: + Application (FILE *in, FILE *out) : + m_window_sp(), + m_screen (NULL), + m_in (in), + m_out (out) + { + + } + + ~Application () + { + m_window_delegates.clear(); + m_window_sp.reset(); + if (m_screen) + { + ::delscreen(m_screen); + m_screen = NULL; + } + } + + void + Initialize () + { + ::setlocale(LC_ALL, ""); + ::setlocale(LC_CTYPE, ""); +#if 0 + ::initscr(); +#else + m_screen = ::newterm(NULL, m_out, m_in); +#endif + ::start_color(); + ::curs_set(0); + ::noecho(); + ::keypad(stdscr,TRUE); + } + + void + Terminate () + { + ::endwin(); + } + + void + Run (Debugger &debugger) + { + bool done = false; + int delay_in_tenths_of_a_second = 1; + + // Alas the threading model in curses is a bit lame so we need to + // resort to polling every 0.5 seconds. We could poll for stdin + // ourselves and then pass the keys down but then we need to + // translate all of the escape sequences ourselves. So we resort to + // polling for input because we need to receive async process events + // while in this loop. + + halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths of seconds seconds when calling Window::GetChar() + + ListenerSP listener_sp (new Listener ("lldb.IOHandler.curses.Application")); + ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); + ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); + ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); + debugger.EnableForwardEvents (listener_sp); + + bool update = true; +#if defined(__APPLE__) + std::deque<int> escape_chars; +#endif + + while (!done) + { + if (update) + { + m_window_sp->Draw(false); + // All windows should be calling Window::DeferredRefresh() instead + // of Window::Refresh() so we can do a single update and avoid + // any screen blinking + update_panels(); + + // Cursor hiding isn't working on MacOSX, so hide it in the top left corner + m_window_sp->MoveCursor(0, 0); + + doupdate(); + update = false; + } + +#if defined(__APPLE__) + // Terminal.app doesn't map its function keys correctly, F1-F4 default to: + // \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if possible + int ch; + if (escape_chars.empty()) + ch = m_window_sp->GetChar(); + else + { + ch = escape_chars.front(); + escape_chars.pop_front(); + } + if (ch == KEY_ESCAPE) + { + int ch2 = m_window_sp->GetChar(); + if (ch2 == 'O') + { + int ch3 = m_window_sp->GetChar(); + switch (ch3) + { + case 'P': ch = KEY_F(1); break; + case 'Q': ch = KEY_F(2); break; + case 'R': ch = KEY_F(3); break; + case 'S': ch = KEY_F(4); break; + default: + escape_chars.push_back(ch2); + if (ch3 != -1) + escape_chars.push_back(ch3); + break; + } + } + else if (ch2 != -1) + escape_chars.push_back(ch2); + } +#else + int ch = m_window_sp->GetChar(); + +#endif + if (ch == -1) + { + if (feof(m_in) || ferror(m_in)) + { + done = true; + } + else + { + // Just a timeout from using halfdelay(), check for events + EventSP event_sp; + while (listener_sp->PeekAtNextEvent()) + { + listener_sp->GetNextEvent(event_sp); + + if (event_sp) + { + Broadcaster *broadcaster = event_sp->GetBroadcaster(); + if (broadcaster) + { + //uint32_t event_type = event_sp->GetType(); + ConstString broadcaster_class (broadcaster->GetBroadcasterClass()); + if (broadcaster_class == broadcaster_class_process) + { + update = true; + continue; // Don't get any key, just update our view + } + } + } + } + } + } + else + { + HandleCharResult key_result = m_window_sp->HandleChar(ch); + switch (key_result) + { + case eKeyHandled: + update = true; + break; + case eKeyNotHandled: + break; + case eQuitApplication: + done = true; + break; + } + } + } + + debugger.CancelForwardEvents (listener_sp); + + } + + WindowSP & + GetMainWindow () + { + if (!m_window_sp) + m_window_sp.reset (new Window ("main", stdscr, false)); + return m_window_sp; + } + + WindowDelegates & + GetWindowDelegates () + { + return m_window_delegates; + } + + protected: + WindowSP m_window_sp; + WindowDelegates m_window_delegates; + SCREEN *m_screen; + FILE *m_in; + FILE *m_out; + }; + + +} // namespace curses + + +using namespace curses; + +struct Row +{ + ValueObjectSP valobj; + Row *parent; + int row_idx; + int x; + int y; + bool might_have_children; + bool expanded; + bool calculated_children; + std::vector<Row> children; + + Row (const ValueObjectSP &v, Row *p) : + valobj (v), + parent (p), + row_idx(0), + x(1), + y(1), + might_have_children (v ? v->MightHaveChildren() : false), + expanded (false), + calculated_children (false), + children() + { + } + + size_t + GetDepth () const + { + if (parent) + return 1 + parent->GetDepth(); + return 0; + } + + void + Expand() + { + expanded = true; + if (!calculated_children) + { + calculated_children = true; + if (valobj) + { + const size_t num_children = valobj->GetNumChildren(); + for (size_t i=0; i<num_children; ++i) + { + children.push_back(Row (valobj->GetChildAtIndex(i, true), this)); + } + } + } + } + + void + Unexpand () + { + expanded = false; + } + + void + DrawTree (Window &window) + { + if (parent) + parent->DrawTreeForChild (window, this, 0); + + if (might_have_children) + { + // It we can get UTF8 characters to work we should try to use the "symbol" + // UTF8 string below +// const char *symbol = ""; +// if (row.expanded) +// symbol = "\xe2\x96\xbd "; +// else +// symbol = "\xe2\x96\xb7 "; +// window.PutCString (symbol); + + // The ACS_DARROW and ACS_RARROW don't look very nice they are just a + // 'v' or '>' character... +// if (expanded) +// window.PutChar (ACS_DARROW); +// else +// window.PutChar (ACS_RARROW); + // Since we can't find any good looking right arrow/down arrow + // symbols, just use a diamond... + window.PutChar (ACS_DIAMOND); + window.PutChar (ACS_HLINE); + } + } + + void + DrawTreeForChild (Window &window, Row *child, uint32_t reverse_depth) + { + if (parent) + parent->DrawTreeForChild (window, this, reverse_depth + 1); + + if (&children.back() == child) + { + // Last child + if (reverse_depth == 0) + { + window.PutChar (ACS_LLCORNER); + window.PutChar (ACS_HLINE); + } + else + { + window.PutChar (' '); + window.PutChar (' '); + } + } + else + { + if (reverse_depth == 0) + { + window.PutChar (ACS_LTEE); + window.PutChar (ACS_HLINE); + } + else + { + window.PutChar (ACS_VLINE); + window.PutChar (' '); + } + } + } +}; + +struct DisplayOptions +{ + bool show_types; +}; + +class TreeItem; + +class TreeDelegate +{ +public: + TreeDelegate() {} + virtual ~TreeDelegate() {} + virtual void TreeDelegateDrawTreeItem (TreeItem &item, Window &window) = 0; + virtual void TreeDelegateGenerateChildren (TreeItem &item) = 0; + virtual bool TreeDelegateItemSelected (TreeItem &item) = 0; // Return true if we need to update views +}; +typedef std::shared_ptr<TreeDelegate> TreeDelegateSP; + +class TreeItem +{ +public: + + TreeItem (TreeItem *parent, TreeDelegate &delegate, bool might_have_children) : + m_parent (parent), + m_delegate (delegate), + m_identifier (0), + m_row_idx (-1), + m_children (), + m_might_have_children (might_have_children), + m_is_expanded (false) + { + } + + TreeItem & + operator=(const TreeItem &rhs) + { + if (this != &rhs) + { + m_parent = rhs.m_parent; + m_delegate = rhs.m_delegate; + m_identifier = rhs.m_identifier; + m_row_idx = rhs.m_row_idx; + m_children = rhs.m_children; + m_might_have_children = rhs.m_might_have_children; + m_is_expanded = rhs.m_is_expanded; + } + return *this; + } + + size_t + GetDepth () const + { + if (m_parent) + return 1 + m_parent->GetDepth(); + return 0; + } + + int + GetRowIndex () const + { + return m_row_idx; + } + + void + ClearChildren () + { + m_children.clear(); + } + + void + Resize (size_t n, const TreeItem &t) + { + m_children.resize(n, t); + } + + TreeItem & + operator [](size_t i) + { + return m_children[i]; + } + + void + SetRowIndex (int row_idx) + { + m_row_idx = row_idx; + } + + size_t + GetNumChildren () + { + m_delegate.TreeDelegateGenerateChildren (*this); + return m_children.size(); + } + + void + ItemWasSelected () + { + m_delegate.TreeDelegateItemSelected(*this); + } + void + CalculateRowIndexes (int &row_idx) + { + SetRowIndex(row_idx); + ++row_idx; + + // The root item must calculate its children + if (m_parent == NULL) + GetNumChildren(); + + const bool expanded = IsExpanded(); + for (auto &item : m_children) + { + if (expanded) + item.CalculateRowIndexes(row_idx); + else + item.SetRowIndex(-1); + } + } + + TreeItem * + GetParent () + { + return m_parent; + } + + bool + IsExpanded () const + { + return m_is_expanded; + } + + void + Expand() + { + m_is_expanded = true; + } + + void + Unexpand () + { + m_is_expanded = false; + } + + bool + Draw (Window &window, + const int first_visible_row, + const uint32_t selected_row_idx, + int &row_idx, + int &num_rows_left) + { + if (num_rows_left <= 0) + return false; + + if (m_row_idx >= first_visible_row) + { + window.MoveCursor(2, row_idx + 1); + + if (m_parent) + m_parent->DrawTreeForChild (window, this, 0); + + if (m_might_have_children) + { + // It we can get UTF8 characters to work we should try to use the "symbol" + // UTF8 string below + // const char *symbol = ""; + // if (row.expanded) + // symbol = "\xe2\x96\xbd "; + // else + // symbol = "\xe2\x96\xb7 "; + // window.PutCString (symbol); + + // The ACS_DARROW and ACS_RARROW don't look very nice they are just a + // 'v' or '>' character... + // if (expanded) + // window.PutChar (ACS_DARROW); + // else + // window.PutChar (ACS_RARROW); + // Since we can't find any good looking right arrow/down arrow + // symbols, just use a diamond... + window.PutChar (ACS_DIAMOND); + window.PutChar (ACS_HLINE); + } + bool highlight = (selected_row_idx == m_row_idx) && window.IsActive(); + + if (highlight) + window.AttributeOn(A_REVERSE); + + m_delegate.TreeDelegateDrawTreeItem(*this, window); + + if (highlight) + window.AttributeOff(A_REVERSE); + ++row_idx; + --num_rows_left; + } + + if (num_rows_left <= 0) + return false; // We are done drawing... + + if (IsExpanded()) + { + for (auto &item : m_children) + { + // If we displayed all the rows and item.Draw() returns + // false we are done drawing and can exit this for loop + if (item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left) == false) + break; + } + } + return num_rows_left >= 0; // Return true if not done drawing yet + } + + void + DrawTreeForChild (Window &window, TreeItem *child, uint32_t reverse_depth) + { + if (m_parent) + m_parent->DrawTreeForChild (window, this, reverse_depth + 1); + + if (&m_children.back() == child) + { + // Last child + if (reverse_depth == 0) + { + window.PutChar (ACS_LLCORNER); + window.PutChar (ACS_HLINE); + } + else + { + window.PutChar (' '); + window.PutChar (' '); + } + } + else + { + if (reverse_depth == 0) + { + window.PutChar (ACS_LTEE); + window.PutChar (ACS_HLINE); + } + else + { + window.PutChar (ACS_VLINE); + window.PutChar (' '); + } + } + } + + TreeItem * + GetItemForRowIndex (uint32_t row_idx) + { + if (m_row_idx == row_idx) + return this; + if (m_children.empty()) + return NULL; + if (m_children.back().m_row_idx < row_idx) + return NULL; + if (IsExpanded()) + { + for (auto &item : m_children) + { + TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx); + if (selected_item_ptr) + return selected_item_ptr; + } + } + return NULL; + } + +// void * +// GetUserData() const +// { +// return m_user_data; +// } +// +// void +// SetUserData (void *user_data) +// { +// m_user_data = user_data; +// } + uint64_t + GetIdentifier() const + { + return m_identifier; + } + + void + SetIdentifier (uint64_t identifier) + { + m_identifier = identifier; + } + + +protected: + TreeItem *m_parent; + TreeDelegate &m_delegate; + //void *m_user_data; + uint64_t m_identifier; + int m_row_idx; // Zero based visible row index, -1 if not visible or for the root item + std::vector<TreeItem> m_children; + bool m_might_have_children; + bool m_is_expanded; + +}; + +class TreeWindowDelegate : public WindowDelegate +{ +public: + TreeWindowDelegate (Debugger &debugger, const TreeDelegateSP &delegate_sp) : + m_debugger (debugger), + m_delegate_sp (delegate_sp), + m_root (NULL, *delegate_sp, true), + m_selected_item (NULL), + m_num_rows (0), + m_selected_row_idx (0), + m_first_visible_row (0), + m_min_x (0), + m_min_y (0), + m_max_x (0), + m_max_y (0) + { + } + + int + NumVisibleRows () const + { + return m_max_y - m_min_y; + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); + Process *process = exe_ctx.GetProcessPtr(); + + bool display_content = false; + if (process) + { + StateType state = process->GetState(); + if (StateIsStoppedState(state, true)) + { + // We are stopped, so it is ok to + display_content = true; + } + else if (StateIsRunningState(state)) + { + return true; // Don't do any updating when we are running + } + } + + m_min_x = 2; + m_min_y = 1; + m_max_x = window.GetWidth() - 1; + m_max_y = window.GetHeight() - 1; + + window.Erase(); + window.DrawTitleBox (window.GetName()); + + if (display_content) + { + const int num_visible_rows = NumVisibleRows(); + m_num_rows = 0; + m_root.CalculateRowIndexes(m_num_rows); + + // If we unexpanded while having something selected our + // total number of rows is less than the num visible rows, + // then make sure we show all the rows by setting the first + // visible row accordingly. + if (m_first_visible_row > 0 && m_num_rows < num_visible_rows) + m_first_visible_row = 0; + + // Make sure the selected row is always visible + if (m_selected_row_idx < m_first_visible_row) + m_first_visible_row = m_selected_row_idx; + else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) + m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; + + int row_idx = 0; + int num_rows_left = num_visible_rows; + m_root.Draw (window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left); + // Get the selected row + m_selected_item = m_root.GetItemForRowIndex (m_selected_row_idx); + } + else + { + m_selected_item = NULL; + } + + window.DeferredRefresh(); + + + return true; // Drawing handled + } + + + virtual const char * + WindowDelegateGetHelpText () + { + return "Thread window keyboard shortcuts:"; + } + + virtual KeyHelp * + WindowDelegateGetKeyHelp () + { + static curses::KeyHelp g_source_view_key_help[] = { + { KEY_UP, "Select previous item" }, + { KEY_DOWN, "Select next item" }, + { KEY_RIGHT, "Expand the selected item" }, + { KEY_LEFT, "Unexpand the selected item or select parent if not expanded" }, + { KEY_PPAGE, "Page up" }, + { KEY_NPAGE, "Page down" }, + { 'h', "Show help dialog" }, + { ' ', "Toggle item expansion" }, + { ',', "Page up" }, + { '.', "Page down" }, + { '\0', NULL } + }; + return g_source_view_key_help; + } + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int c) + { + switch(c) + { + case ',': + case KEY_PPAGE: + // Page up key + if (m_first_visible_row > 0) + { + if (m_first_visible_row > m_max_y) + m_first_visible_row -= m_max_y; + else + m_first_visible_row = 0; + m_selected_row_idx = m_first_visible_row; + m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); + if (m_selected_item) + m_selected_item->ItemWasSelected (); + } + return eKeyHandled; + + case '.': + case KEY_NPAGE: + // Page down key + if (m_num_rows > m_max_y) + { + if (m_first_visible_row + m_max_y < m_num_rows) + { + m_first_visible_row += m_max_y; + m_selected_row_idx = m_first_visible_row; + m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); + if (m_selected_item) + m_selected_item->ItemWasSelected (); + } + } + return eKeyHandled; + + case KEY_UP: + if (m_selected_row_idx > 0) + { + --m_selected_row_idx; + m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); + if (m_selected_item) + m_selected_item->ItemWasSelected (); + } + return eKeyHandled; + case KEY_DOWN: + if (m_selected_row_idx + 1 < m_num_rows) + { + ++m_selected_row_idx; + m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); + if (m_selected_item) + m_selected_item->ItemWasSelected (); + } + return eKeyHandled; + + case KEY_RIGHT: + if (m_selected_item) + { + if (!m_selected_item->IsExpanded()) + m_selected_item->Expand(); + } + return eKeyHandled; + + case KEY_LEFT: + if (m_selected_item) + { + if (m_selected_item->IsExpanded()) + m_selected_item->Unexpand(); + else if (m_selected_item->GetParent()) + { + m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex(); + m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); + if (m_selected_item) + m_selected_item->ItemWasSelected (); + } + } + return eKeyHandled; + + case ' ': + // Toggle expansion state when SPACE is pressed + if (m_selected_item) + { + if (m_selected_item->IsExpanded()) + m_selected_item->Unexpand(); + else + m_selected_item->Expand(); + } + return eKeyHandled; + + case 'h': + window.CreateHelpSubwindow (); + return eKeyHandled; + + default: + break; + } + return eKeyNotHandled; + } + +protected: + Debugger &m_debugger; + TreeDelegateSP m_delegate_sp; + TreeItem m_root; + TreeItem *m_selected_item; + int m_num_rows; + int m_selected_row_idx; + int m_first_visible_row; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + +}; + +class FrameTreeDelegate : public TreeDelegate +{ +public: + FrameTreeDelegate (const ThreadSP &thread_sp) : + TreeDelegate(), + m_thread_wp() + { + if (thread_sp) + m_thread_wp = thread_sp; + } + + virtual ~FrameTreeDelegate() + { + } + + virtual void + TreeDelegateDrawTreeItem (TreeItem &item, Window &window) + { + ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + { + const uint64_t frame_idx = item.GetIdentifier(); + StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(frame_idx); + if (frame_sp) + { + StreamString strm; + const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything); + ExecutionContext exe_ctx (frame_sp); + //const char *frame_format = "frame #${frame.index}: ${module.file.basename}{`${function.name}${function.pc-offset}}}"; + const char *frame_format = "frame #${frame.index}: {${function.name}${function.pc-offset}}}"; + if (Debugger::FormatPrompt (frame_format, &sc, &exe_ctx, NULL, strm)) + { + int right_pad = 1; + window.PutCStringTruncated(strm.GetString().c_str(), right_pad); + } + } + } + } + virtual void + TreeDelegateGenerateChildren (TreeItem &item) + { + // No children for frames yet... + } + + virtual bool + TreeDelegateItemSelected (TreeItem &item) + { + ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + { + const uint64_t frame_idx = item.GetIdentifier(); + thread_sp->SetSelectedFrameByIndex(frame_idx); + return true; + } + return false; + } + void + SetThread (ThreadSP thread_sp) + { + m_thread_wp = thread_sp; + } + +protected: + ThreadWP m_thread_wp; +}; + +class ThreadTreeDelegate : public TreeDelegate +{ +public: + ThreadTreeDelegate (Debugger &debugger) : + TreeDelegate(), + m_debugger (debugger), + m_thread_wp (), + m_tid (LLDB_INVALID_THREAD_ID), + m_stop_id (UINT32_MAX) + { + } + + virtual + ~ThreadTreeDelegate() + { + } + + virtual void + TreeDelegateDrawTreeItem (TreeItem &item, Window &window) + { + ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + { + StreamString strm; + ExecutionContext exe_ctx (thread_sp); + const char *format = "thread #${thread.index}: tid = ${thread.id}{, stop reason = ${thread.stop-reason}}"; + if (Debugger::FormatPrompt (format, NULL, &exe_ctx, NULL, strm)) + { + int right_pad = 1; + window.PutCStringTruncated(strm.GetString().c_str(), right_pad); + } + } + } + virtual void + TreeDelegateGenerateChildren (TreeItem &item) + { + TargetSP target_sp (m_debugger.GetSelectedTarget()); + if (target_sp) + { + ProcessSP process_sp = target_sp->GetProcessSP(); + if (process_sp && process_sp->IsAlive()) + { + StateType state = process_sp->GetState(); + if (StateIsStoppedState(state, true)) + { + ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread(); + if (thread_sp) + { + if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid) + return; // Children are already up to date + if (m_frame_delegate_sp) + m_frame_delegate_sp->SetThread(thread_sp); + else + { + // Always expand the thread item the first time we show it + item.Expand(); + m_frame_delegate_sp.reset (new FrameTreeDelegate(thread_sp)); + } + + m_stop_id = process_sp->GetStopID(); + m_thread_wp = thread_sp; + m_tid = thread_sp->GetID(); + + TreeItem t (&item, *m_frame_delegate_sp, false); + size_t num_frames = thread_sp->GetStackFrameCount(); + item.Resize (num_frames, t); + for (size_t i=0; i<num_frames; ++i) + { + item[i].SetIdentifier(i); + } + } + return; + } + } + } + item.ClearChildren(); + } + + virtual bool + TreeDelegateItemSelected (TreeItem &item) + { + ThreadSP thread_sp = m_thread_wp.lock(); + if (thread_sp) + { + ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); + Mutex::Locker locker (thread_list.GetMutex()); + ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); + if (selected_thread_sp->GetID() != thread_sp->GetID()) + { + thread_list.SetSelectedThreadByID(thread_sp->GetID()); + return true; + } + } + return false; + } + +protected: + Debugger &m_debugger; + ThreadWP m_thread_wp; + std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp; + lldb::user_id_t m_tid; + uint32_t m_stop_id; +}; + +class ValueObjectListDelegate : public WindowDelegate +{ +public: + ValueObjectListDelegate () : + m_valobj_list (), + m_rows (), + m_selected_row (NULL), + m_selected_row_idx (0), + m_first_visible_row (0), + m_num_rows (0), + m_max_x (0), + m_max_y (0) + { + } + + ValueObjectListDelegate (ValueObjectList &valobj_list) : + m_valobj_list (valobj_list), + m_rows (), + m_selected_row (NULL), + m_selected_row_idx (0), + m_first_visible_row (0), + m_num_rows (0), + m_max_x (0), + m_max_y (0) + { + SetValues (valobj_list); + } + + virtual + ~ValueObjectListDelegate() + { + } + + void + SetValues (ValueObjectList &valobj_list) + { + m_selected_row = NULL; + m_selected_row_idx = 0; + m_first_visible_row = 0; + m_num_rows = 0; + m_rows.clear(); + m_valobj_list = valobj_list; + const size_t num_values = m_valobj_list.GetSize(); + for (size_t i=0; i<num_values; ++i) + m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL)); + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + m_num_rows = 0; + m_min_x = 2; + m_min_y = 1; + m_max_x = window.GetWidth() - 1; + m_max_y = window.GetHeight() - 1; + + window.Erase(); + window.DrawTitleBox (window.GetName()); + + const int num_visible_rows = NumVisibleRows(); + const int num_rows = CalculateTotalNumberRows (m_rows); + + // If we unexpanded while having something selected our + // total number of rows is less than the num visible rows, + // then make sure we show all the rows by setting the first + // visible row accordingly. + if (m_first_visible_row > 0 && num_rows < num_visible_rows) + m_first_visible_row = 0; + + // Make sure the selected row is always visible + if (m_selected_row_idx < m_first_visible_row) + m_first_visible_row = m_selected_row_idx; + else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) + m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; + + DisplayRows (window, m_rows, g_options); + + window.DeferredRefresh(); + + // Get the selected row + m_selected_row = GetRowForRowIndex (m_selected_row_idx); + // Keep the cursor on the selected row so the highlight and the cursor + // are always on the same line + if (m_selected_row) + window.MoveCursor (m_selected_row->x, + m_selected_row->y); + + return true; // Drawing handled + } + + virtual KeyHelp * + WindowDelegateGetKeyHelp () + { + static curses::KeyHelp g_source_view_key_help[] = { + { KEY_UP, "Select previous item" }, + { KEY_DOWN, "Select next item" }, + { KEY_RIGHT, "Expand selected item" }, + { KEY_LEFT, "Unexpand selected item or select parent if not expanded" }, + { KEY_PPAGE, "Page up" }, + { KEY_NPAGE, "Page down" }, + { 'A', "Format as annotated address" }, + { 'b', "Format as binary" }, + { 'B', "Format as hex bytes with ASCII" }, + { 'c', "Format as character" }, + { 'd', "Format as a signed integer" }, + { 'D', "Format selected value using the default format for the type" }, + { 'f', "Format as float" }, + { 'h', "Show help dialog" }, + { 'i', "Format as instructions" }, + { 'o', "Format as octal" }, + { 'p', "Format as pointer" }, + { 's', "Format as C string" }, + { 't', "Toggle showing/hiding type names" }, + { 'u', "Format as an unsigned integer" }, + { 'x', "Format as hex" }, + { 'X', "Format as uppercase hex" }, + { ' ', "Toggle item expansion" }, + { ',', "Page up" }, + { '.', "Page down" }, + { '\0', NULL } + }; + return g_source_view_key_help; + } + + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int c) + { + switch(c) + { + case 'x': + case 'X': + case 'o': + case 's': + case 'u': + case 'd': + case 'D': + case 'i': + case 'A': + case 'p': + case 'c': + case 'b': + case 'B': + case 'f': + // Change the format for the currently selected item + if (m_selected_row) + m_selected_row->valobj->SetFormat (FormatForChar (c)); + return eKeyHandled; + + case 't': + // Toggle showing type names + g_options.show_types = !g_options.show_types; + return eKeyHandled; + + case ',': + case KEY_PPAGE: + // Page up key + if (m_first_visible_row > 0) + { + if (m_first_visible_row > m_max_y) + m_first_visible_row -= m_max_y; + else + m_first_visible_row = 0; + m_selected_row_idx = m_first_visible_row; + } + return eKeyHandled; + + case '.': + case KEY_NPAGE: + // Page down key + if (m_num_rows > m_max_y) + { + if (m_first_visible_row + m_max_y < m_num_rows) + { + m_first_visible_row += m_max_y; + m_selected_row_idx = m_first_visible_row; + } + } + return eKeyHandled; + + case KEY_UP: + if (m_selected_row_idx > 0) + --m_selected_row_idx; + return eKeyHandled; + case KEY_DOWN: + if (m_selected_row_idx + 1 < m_num_rows) + ++m_selected_row_idx; + return eKeyHandled; + + case KEY_RIGHT: + if (m_selected_row) + { + if (!m_selected_row->expanded) + m_selected_row->Expand(); + } + return eKeyHandled; + + case KEY_LEFT: + if (m_selected_row) + { + if (m_selected_row->expanded) + m_selected_row->Unexpand(); + else if (m_selected_row->parent) + m_selected_row_idx = m_selected_row->parent->row_idx; + } + return eKeyHandled; + + case ' ': + // Toggle expansion state when SPACE is pressed + if (m_selected_row) + { + if (m_selected_row->expanded) + m_selected_row->Unexpand(); + else + m_selected_row->Expand(); + } + return eKeyHandled; + + case 'h': + window.CreateHelpSubwindow (); + return eKeyHandled; + + default: + break; + } + return eKeyNotHandled; + } + +protected: + ValueObjectList m_valobj_list; + std::vector<Row> m_rows; + Row *m_selected_row; + uint32_t m_selected_row_idx; + uint32_t m_first_visible_row; + uint32_t m_num_rows; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + + static Format + FormatForChar (int c) + { + switch (c) + { + case 'x': return eFormatHex; + case 'X': return eFormatHexUppercase; + case 'o': return eFormatOctal; + case 's': return eFormatCString; + case 'u': return eFormatUnsigned; + case 'd': return eFormatDecimal; + case 'D': return eFormatDefault; + case 'i': return eFormatInstruction; + case 'A': return eFormatAddressInfo; + case 'p': return eFormatPointer; + case 'c': return eFormatChar; + case 'b': return eFormatBinary; + case 'B': return eFormatBytesWithASCII; + case 'f': return eFormatFloat; + } + return eFormatDefault; + } + + bool + DisplayRowObject (Window &window, + Row &row, + DisplayOptions &options, + bool highlight, + bool last_child) + { + ValueObject *valobj = row.valobj.get(); + + if (valobj == NULL) + return false; + + const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : NULL; + const char *name = valobj->GetName().GetCString(); + const char *value = valobj->GetValueAsCString (); + const char *summary = valobj->GetSummaryAsCString (); + + window.MoveCursor (row.x, row.y); + + row.DrawTree (window); + + if (highlight) + window.AttributeOn(A_REVERSE); + + if (type_name && type_name[0]) + window.Printf ("(%s) ", type_name); + + if (name && name[0]) + window.PutCString(name); + + attr_t changd_attr = 0; + if (valobj->GetValueDidChange()) + changd_attr = COLOR_PAIR(5) | A_BOLD; + + if (value && value[0]) + { + window.PutCString(" = "); + if (changd_attr) + window.AttributeOn(changd_attr); + window.PutCString (value); + if (changd_attr) + window.AttributeOff(changd_attr); + } + + if (summary && summary[0]) + { + window.PutChar(' '); + if (changd_attr) + window.AttributeOn(changd_attr); + window.PutCString(summary); + if (changd_attr) + window.AttributeOff(changd_attr); + } + + if (highlight) + window.AttributeOff (A_REVERSE); + + return true; + } + void + DisplayRows (Window &window, + std::vector<Row> &rows, + DisplayOptions &options) + { + // > 0x25B7 + // \/ 0x25BD + + bool window_is_active = window.IsActive(); + for (auto &row : rows) + { + const bool last_child = row.parent && &rows[rows.size()-1] == &row; + // Save the row index in each Row structure + row.row_idx = m_num_rows; + if ((m_num_rows >= m_first_visible_row) && + ((m_num_rows - m_first_visible_row) < NumVisibleRows())) + { + row.x = m_min_x; + row.y = m_num_rows - m_first_visible_row + 1; + if (DisplayRowObject (window, + row, + options, + window_is_active && m_num_rows == m_selected_row_idx, + last_child)) + { + ++m_num_rows; + } + else + { + row.x = 0; + row.y = 0; + } + } + else + { + row.x = 0; + row.y = 0; + ++m_num_rows; + } + + if (row.expanded && !row.children.empty()) + { + DisplayRows (window, + row.children, + options); + } + } + } + + int + CalculateTotalNumberRows (const std::vector<Row> &rows) + { + int row_count = 0; + for (const auto &row : rows) + { + ++row_count; + if (row.expanded) + row_count += CalculateTotalNumberRows(row.children); + } + return row_count; + } + static Row * + GetRowForRowIndexImpl (std::vector<Row> &rows, size_t &row_index) + { + for (auto &row : rows) + { + if (row_index == 0) + return &row; + else + { + --row_index; + if (row.expanded && !row.children.empty()) + { + Row *result = GetRowForRowIndexImpl (row.children, row_index); + if (result) + return result; + } + } + } + return NULL; + } + + Row * + GetRowForRowIndex (size_t row_index) + { + return GetRowForRowIndexImpl (m_rows, row_index); + } + + int + NumVisibleRows () const + { + return m_max_y - m_min_y; + } + + static DisplayOptions g_options; +}; + +class FrameVariablesWindowDelegate : public ValueObjectListDelegate +{ +public: + FrameVariablesWindowDelegate (Debugger &debugger) : + ValueObjectListDelegate (), + m_debugger (debugger), + m_frame_block (NULL) + { + } + + virtual + ~FrameVariablesWindowDelegate() + { + } + + virtual const char * + WindowDelegateGetHelpText () + { + return "Frame variable window keyboard shortcuts:"; + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); + Process *process = exe_ctx.GetProcessPtr(); + Block *frame_block = NULL; + StackFrame *frame = NULL; + + if (process) + { + StateType state = process->GetState(); + if (StateIsStoppedState(state, true)) + { + frame = exe_ctx.GetFramePtr(); + if (frame) + frame_block = frame->GetFrameBlock (); + } + else if (StateIsRunningState(state)) + { + return true; // Don't do any updating when we are running + } + } + + ValueObjectList local_values; + if (frame_block) + { + // Only update the variables if they have changed + if (m_frame_block != frame_block) + { + m_frame_block = frame_block; + + VariableList *locals = frame->GetVariableList(true); + if (locals) + { + const DynamicValueType use_dynamic = eDynamicDontRunTarget; + const size_t num_locals = locals->GetSize(); + for (size_t i=0; i<num_locals; ++i) + local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic)); + // Update the values + SetValues(local_values); + } + } + } + else + { + m_frame_block = NULL; + // Update the values with an empty list if there is no frame + SetValues(local_values); + } + + return ValueObjectListDelegate::WindowDelegateDraw (window, force); + + } + +protected: + Debugger &m_debugger; + Block *m_frame_block; +}; + + +class RegistersWindowDelegate : public ValueObjectListDelegate +{ +public: + RegistersWindowDelegate (Debugger &debugger) : + ValueObjectListDelegate (), + m_debugger (debugger) + { + } + + virtual + ~RegistersWindowDelegate() + { + } + + virtual const char * + WindowDelegateGetHelpText () + { + return "Register window keyboard shortcuts:"; + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); + StackFrame *frame = exe_ctx.GetFramePtr(); + + ValueObjectList value_list; + if (frame) + { + if (frame->GetStackID() != m_stack_id) + { + m_stack_id = frame->GetStackID(); + RegisterContextSP reg_ctx (frame->GetRegisterContext()); + if (reg_ctx) + { + const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); + for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) + { + value_list.Append(ValueObjectRegisterSet::Create (frame, reg_ctx, set_idx)); + } + } + SetValues(value_list); + } + } + else + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive()) + return true; // Don't do any updating if we are running + else + { + // Update the values with an empty list if there + // is no process or the process isn't alive anymore + SetValues(value_list); + } + } + return ValueObjectListDelegate::WindowDelegateDraw (window, force); + } + +protected: + Debugger &m_debugger; + StackID m_stack_id; +}; + +static const char * +CursesKeyToCString (int ch) +{ + static char g_desc[32]; + if (ch >= KEY_F0 && ch < KEY_F0 + 64) + { + snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0); + return g_desc; + } + switch (ch) + { + case KEY_DOWN: return "down"; + case KEY_UP: return "up"; + case KEY_LEFT: return "left"; + case KEY_RIGHT: return "right"; + case KEY_HOME: return "home"; + case KEY_BACKSPACE: return "backspace"; + case KEY_DL: return "delete-line"; + case KEY_IL: return "insert-line"; + case KEY_DC: return "delete-char"; + case KEY_IC: return "insert-char"; + case KEY_CLEAR: return "clear"; + case KEY_EOS: return "clear-to-eos"; + case KEY_EOL: return "clear-to-eol"; + case KEY_SF: return "scroll-forward"; + case KEY_SR: return "scroll-backward"; + case KEY_NPAGE: return "page-down"; + case KEY_PPAGE: return "page-up"; + case KEY_STAB: return "set-tab"; + case KEY_CTAB: return "clear-tab"; + case KEY_CATAB: return "clear-all-tabs"; + case KEY_ENTER: return "enter"; + case KEY_PRINT: return "print"; + case KEY_LL: return "lower-left key"; + case KEY_A1: return "upper left of keypad"; + case KEY_A3: return "upper right of keypad"; + case KEY_B2: return "center of keypad"; + case KEY_C1: return "lower left of keypad"; + case KEY_C3: return "lower right of keypad"; + case KEY_BTAB: return "back-tab key"; + case KEY_BEG: return "begin key"; + case KEY_CANCEL: return "cancel key"; + case KEY_CLOSE: return "close key"; + case KEY_COMMAND: return "command key"; + case KEY_COPY: return "copy key"; + case KEY_CREATE: return "create key"; + case KEY_END: return "end key"; + case KEY_EXIT: return "exit key"; + case KEY_FIND: return "find key"; + case KEY_HELP: return "help key"; + case KEY_MARK: return "mark key"; + case KEY_MESSAGE: return "message key"; + case KEY_MOVE: return "move key"; + case KEY_NEXT: return "next key"; + case KEY_OPEN: return "open key"; + case KEY_OPTIONS: return "options key"; + case KEY_PREVIOUS: return "previous key"; + case KEY_REDO: return "redo key"; + case KEY_REFERENCE: return "reference key"; + case KEY_REFRESH: return "refresh key"; + case KEY_REPLACE: return "replace key"; + case KEY_RESTART: return "restart key"; + case KEY_RESUME: return "resume key"; + case KEY_SAVE: return "save key"; + case KEY_SBEG: return "shifted begin key"; + case KEY_SCANCEL: return "shifted cancel key"; + case KEY_SCOMMAND: return "shifted command key"; + case KEY_SCOPY: return "shifted copy key"; + case KEY_SCREATE: return "shifted create key"; + case KEY_SDC: return "shifted delete-character key"; + case KEY_SDL: return "shifted delete-line key"; + case KEY_SELECT: return "select key"; + case KEY_SEND: return "shifted end key"; + case KEY_SEOL: return "shifted clear-to-end-of-line key"; + case KEY_SEXIT: return "shifted exit key"; + case KEY_SFIND: return "shifted find key"; + case KEY_SHELP: return "shifted help key"; + case KEY_SHOME: return "shifted home key"; + case KEY_SIC: return "shifted insert-character key"; + case KEY_SLEFT: return "shifted left-arrow key"; + case KEY_SMESSAGE: return "shifted message key"; + case KEY_SMOVE: return "shifted move key"; + case KEY_SNEXT: return "shifted next key"; + case KEY_SOPTIONS: return "shifted options key"; + case KEY_SPREVIOUS: return "shifted previous key"; + case KEY_SPRINT: return "shifted print key"; + case KEY_SREDO: return "shifted redo key"; + case KEY_SREPLACE: return "shifted replace key"; + case KEY_SRIGHT: return "shifted right-arrow key"; + case KEY_SRSUME: return "shifted resume key"; + case KEY_SSAVE: return "shifted save key"; + case KEY_SSUSPEND: return "shifted suspend key"; + case KEY_SUNDO: return "shifted undo key"; + case KEY_SUSPEND: return "suspend key"; + case KEY_UNDO: return "undo key"; + case KEY_MOUSE: return "Mouse event has occurred"; + case KEY_RESIZE: return "Terminal resize event"; + case KEY_EVENT: return "We were interrupted by an event"; + case KEY_RETURN: return "return"; + case ' ': return "space"; + case '\t': return "tab"; + case KEY_ESCAPE: return "escape"; + default: + if (isprint(ch)) + snprintf(g_desc, sizeof(g_desc), "%c", ch); + else + snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch); + return g_desc; + } + return NULL; +} + +HelpDialogDelegate::HelpDialogDelegate (const char *text, KeyHelp *key_help_array) : + m_text (), + m_first_visible_line (0) +{ + if (text && text[0]) + { + m_text.SplitIntoLines(text); + m_text.AppendString(""); + } + if (key_help_array) + { + for (KeyHelp *key = key_help_array; key->ch; ++key) + { + StreamString key_description; + key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description); + m_text.AppendString(std::move(key_description.GetString())); + } + } +} + +HelpDialogDelegate::~HelpDialogDelegate() +{ +} + +bool +HelpDialogDelegate::WindowDelegateDraw (Window &window, bool force) +{ + window.Erase(); + const int window_height = window.GetHeight(); + int x = 2; + int y = 1; + const int min_y = y; + const int max_y = window_height - 1 - y; + const int num_visible_lines = max_y - min_y + 1; + const size_t num_lines = m_text.GetSize(); + const char *bottom_message; + if (num_lines <= num_visible_lines) + bottom_message = "Press any key to exit"; + else + bottom_message = "Use arrows to scroll, any other key to exit"; + window.DrawTitleBox(window.GetName(), bottom_message); + while (y <= max_y) + { + window.MoveCursor(x, y); + window.PutCStringTruncated(m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1); + ++y; + } + return true; +} + +HandleCharResult +HelpDialogDelegate::WindowDelegateHandleChar (Window &window, int key) +{ + bool done = false; + const size_t num_lines = m_text.GetSize(); + const size_t num_visible_lines = window.GetHeight() - 2; + + if (num_lines <= num_visible_lines) + { + done = true; + // If we have all lines visible and don't need scrolling, then any + // key press will cause us to exit + } + else + { + switch (key) + { + case KEY_UP: + if (m_first_visible_line > 0) + --m_first_visible_line; + break; + + case KEY_DOWN: + if (m_first_visible_line + num_visible_lines < num_lines) + ++m_first_visible_line; + break; + + case KEY_PPAGE: + case ',': + if (m_first_visible_line > 0) + { + if (m_first_visible_line >= num_visible_lines) + m_first_visible_line -= num_visible_lines; + else + m_first_visible_line = 0; + } + break; + case KEY_NPAGE: + case '.': + if (m_first_visible_line + num_visible_lines < num_lines) + { + m_first_visible_line += num_visible_lines; + if (m_first_visible_line > num_lines) + m_first_visible_line = num_lines - num_visible_lines; + } + break; + default: + done = true; + break; + } + } + if (done) + window.GetParent()->RemoveSubWindow(&window); + return eKeyHandled; +} + +class ApplicationDelegate : + public WindowDelegate, + public MenuDelegate +{ +public: + enum { + eMenuID_LLDB = 1, + eMenuID_LLDBAbout, + eMenuID_LLDBExit, + + eMenuID_Target, + eMenuID_TargetCreate, + eMenuID_TargetDelete, + + eMenuID_Process, + eMenuID_ProcessAttach, + eMenuID_ProcessDetach, + eMenuID_ProcessLaunch, + eMenuID_ProcessContinue, + eMenuID_ProcessHalt, + eMenuID_ProcessKill, + + eMenuID_Thread, + eMenuID_ThreadStepIn, + eMenuID_ThreadStepOver, + eMenuID_ThreadStepOut, + + eMenuID_View, + eMenuID_ViewBacktrace, + eMenuID_ViewRegisters, + eMenuID_ViewSource, + eMenuID_ViewVariables, + + eMenuID_Help, + eMenuID_HelpGUIHelp + }; + + ApplicationDelegate (Application &app, Debugger &debugger) : + WindowDelegate (), + MenuDelegate (), + m_app (app), + m_debugger (debugger) + { + } + + virtual + ~ApplicationDelegate () + { + } + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + return false; // Drawing not handled, let standard window drawing happen + } + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int key) + { + switch (key) + { + case '\t': + window.SelectNextWindowAsActive(); + return eKeyHandled; + + case 'h': + window.CreateHelpSubwindow(); + return eKeyHandled; + + case KEY_ESCAPE: + return eQuitApplication; + + default: + break; + } + return eKeyNotHandled; + } + + + virtual const char * + WindowDelegateGetHelpText () + { + return "Welcome to the LLDB curses GUI.\n\n" + "Press the TAB key to change the selected view.\n" + "Each view has its own keyboard shortcuts, press 'h' to open a dialog to display them.\n\n" + "Common key bindings for all views:"; + } + + virtual KeyHelp * + WindowDelegateGetKeyHelp () + { + static curses::KeyHelp g_source_view_key_help[] = { + { '\t', "Select next view" }, + { 'h', "Show help dialog with view specific key bindings" }, + { ',', "Page up" }, + { '.', "Page down" }, + { KEY_UP, "Select previous" }, + { KEY_DOWN, "Select next" }, + { KEY_LEFT, "Unexpand or select parent" }, + { KEY_RIGHT, "Expand" }, + { KEY_PPAGE, "Page up" }, + { KEY_NPAGE, "Page down" }, + { '\0', NULL } + }; + return g_source_view_key_help; + } + + virtual MenuActionResult + MenuDelegateAction (Menu &menu) + { + switch (menu.GetIdentifier()) + { + case eMenuID_ThreadStepIn: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasThreadScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) + exe_ctx.GetThreadRef().StepIn(true, true); + } + } + return MenuActionResult::Handled; + + case eMenuID_ThreadStepOut: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasThreadScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) + exe_ctx.GetThreadRef().StepOut(); + } + } + return MenuActionResult::Handled; + + case eMenuID_ThreadStepOver: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasThreadScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) + exe_ctx.GetThreadRef().StepOver(true); + } + } + return MenuActionResult::Handled; + + case eMenuID_ProcessContinue: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) + process->Resume(); + } + } + return MenuActionResult::Handled; + + case eMenuID_ProcessKill: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive()) + process->Destroy(); + } + } + return MenuActionResult::Handled; + + case eMenuID_ProcessHalt: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive()) + process->Halt(); + } + } + return MenuActionResult::Handled; + + case eMenuID_ProcessDetach: + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + { + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive()) + process->Detach(false); + } + } + return MenuActionResult::Handled; + + case eMenuID_Process: + { + // Populate the menu with all of the threads if the process is stopped when + // the Process menu gets selected and is about to display its submenu. + Menus &submenus = menu.GetSubmenus(); + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + Process *process = exe_ctx.GetProcessPtr(); + if (process && process->IsAlive() && StateIsStoppedState (process->GetState(), true)) + { + if (submenus.size() == 7) + menu.AddSubmenu (MenuSP (new Menu(Menu::Type::Separator))); + else if (submenus.size() > 8) + submenus.erase (submenus.begin() + 8, submenus.end()); + + ThreadList &threads = process->GetThreadList(); + Mutex::Locker locker (threads.GetMutex()); + size_t num_threads = threads.GetSize(); + for (size_t i=0; i<num_threads; ++i) + { + ThreadSP thread_sp = threads.GetThreadAtIndex(i); + char menu_char = '\0'; + if (i < 9) + menu_char = '1' + i; + StreamString thread_menu_title; + thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID()); + const char *thread_name = thread_sp->GetName(); + if (thread_name && thread_name[0]) + thread_menu_title.Printf (" %s", thread_name); + else + { + const char *queue_name = thread_sp->GetQueueName(); + if (queue_name && queue_name[0]) + thread_menu_title.Printf (" %s", queue_name); + } + menu.AddSubmenu (MenuSP (new Menu(thread_menu_title.GetString().c_str(), NULL, menu_char, thread_sp->GetID()))); + } + } + else if (submenus.size() > 7) + { + // Remove the separator and any other thread submenu items + // that were previously added + submenus.erase (submenus.begin() + 7, submenus.end()); + } + // Since we are adding and removing items we need to recalculate the name lengths + menu.RecalculateNameLengths(); + } + return MenuActionResult::Handled; + + case eMenuID_ViewVariables: + { + WindowSP main_window_sp = m_app.GetMainWindow(); + WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); + WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); + WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); + const Rect source_bounds = source_window_sp->GetBounds(); + + if (variables_window_sp) + { + const Rect variables_bounds = variables_window_sp->GetBounds(); + + main_window_sp->RemoveSubWindow(variables_window_sp.get()); + + if (registers_window_sp) + { + // We have a registers window, so give all the area back to the registers window + Rect registers_bounds = variables_bounds; + registers_bounds.size.width = source_bounds.size.width; + registers_window_sp->SetBounds(registers_bounds); + } + else + { + // We have no registers window showing so give the bottom + // area back to the source view + source_window_sp->Resize (source_bounds.size.width, + source_bounds.size.height + variables_bounds.size.height); + } + } + else + { + Rect new_variables_rect; + if (registers_window_sp) + { + // We have a registers window so split the area of the registers + // window into two columns where the left hand side will be the + // variables and the right hand side will be the registers + const Rect variables_bounds = registers_window_sp->GetBounds(); + Rect new_registers_rect; + variables_bounds.VerticalSplitPercentage (0.50, new_variables_rect, new_registers_rect); + registers_window_sp->SetBounds (new_registers_rect); + } + else + { + // No variables window, grab the bottom part of the source window + Rect new_source_rect; + source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_variables_rect); + source_window_sp->SetBounds (new_source_rect); + } + WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Variables", + new_variables_rect, + false); + new_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); + } + touchwin(stdscr); + } + return MenuActionResult::Handled; + + case eMenuID_ViewRegisters: + { + WindowSP main_window_sp = m_app.GetMainWindow(); + WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); + WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); + WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); + const Rect source_bounds = source_window_sp->GetBounds(); + + if (registers_window_sp) + { + if (variables_window_sp) + { + const Rect variables_bounds = variables_window_sp->GetBounds(); + + // We have a variables window, so give all the area back to the variables window + variables_window_sp->Resize (variables_bounds.size.width + registers_window_sp->GetWidth(), + variables_bounds.size.height); + } + else + { + // We have no variables window showing so give the bottom + // area back to the source view + source_window_sp->Resize (source_bounds.size.width, + source_bounds.size.height + registers_window_sp->GetHeight()); + } + main_window_sp->RemoveSubWindow(registers_window_sp.get()); + } + else + { + Rect new_regs_rect; + if (variables_window_sp) + { + // We have a variables window, split it into two columns + // where the left hand side will be the variables and the + // right hand side will be the registers + const Rect variables_bounds = variables_window_sp->GetBounds(); + Rect new_vars_rect; + variables_bounds.VerticalSplitPercentage (0.50, new_vars_rect, new_regs_rect); + variables_window_sp->SetBounds (new_vars_rect); + } + else + { + // No registers window, grab the bottom part of the source window + Rect new_source_rect; + source_bounds.HorizontalSplitPercentage (0.70, new_source_rect, new_regs_rect); + source_window_sp->SetBounds (new_source_rect); + } + WindowSP new_window_sp = main_window_sp->CreateSubWindow ("Registers", + new_regs_rect, + false); + new_window_sp->SetDelegate (WindowDelegateSP(new RegistersWindowDelegate(m_debugger))); + } + touchwin(stdscr); + } + return MenuActionResult::Handled; + + case eMenuID_HelpGUIHelp: + m_app.GetMainWindow ()->CreateHelpSubwindow(); + return MenuActionResult::Handled; + + default: + break; + } + + return MenuActionResult::NotHandled; + } +protected: + Application &m_app; + Debugger &m_debugger; +}; + + +class StatusBarWindowDelegate : public WindowDelegate +{ +public: + StatusBarWindowDelegate (Debugger &debugger) : + m_debugger (debugger) + { + } + + virtual + ~StatusBarWindowDelegate () + { + } + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + StackFrame *frame = exe_ctx.GetFramePtr(); + window.Erase(); + window.SetBackground(2); + window.MoveCursor (0, 0); + if (process) + { + const StateType state = process->GetState(); + window.Printf ("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state)); + + if (StateIsStoppedState(state, true)) + { + window.MoveCursor (40, 0); + if (thread) + window.Printf ("Thread: 0x%4.4" PRIx64, thread->GetID()); + + window.MoveCursor (60, 0); + if (frame) + window.Printf ("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress (exe_ctx.GetTargetPtr())); + } + else if (state == eStateExited) + { + const char *exit_desc = process->GetExitDescription(); + const int exit_status = process->GetExitStatus(); + if (exit_desc && exit_desc[0]) + window.Printf (" with status = %i (%s)", exit_status, exit_desc); + else + window.Printf (" with status = %i", exit_status); + } + } + window.DeferredRefresh(); + return true; + } + +protected: + Debugger &m_debugger; +}; + +class SourceFileWindowDelegate : public WindowDelegate +{ +public: + SourceFileWindowDelegate (Debugger &debugger) : + WindowDelegate (), + m_debugger (debugger), + m_sc (), + m_file_sp (), + m_disassembly_scope (NULL), + m_disassembly_sp (), + m_disassembly_range (), + m_line_width (4), + m_selected_line (0), + m_pc_line (0), + m_stop_id (0), + m_frame_idx (UINT32_MAX), + m_first_visible_line (0), + m_min_x (0), + m_min_y (0), + m_max_x (0), + m_max_y (0) + { + } + + + virtual + ~SourceFileWindowDelegate() + { + } + + void + Update (const SymbolContext &sc) + { + m_sc = sc; + } + + uint32_t + NumVisibleLines () const + { + return m_max_y - m_min_y; + } + + virtual const char * + WindowDelegateGetHelpText () + { + return "Source/Disassembly window keyboard shortcuts:"; + } + + virtual KeyHelp * + WindowDelegateGetKeyHelp () + { + static curses::KeyHelp g_source_view_key_help[] = { + { KEY_RETURN, "Run to selected line with one shot breakpoint" }, + { KEY_UP, "Select previous source line" }, + { KEY_DOWN, "Select next source line" }, + { KEY_PPAGE, "Page up" }, + { KEY_NPAGE, "Page down" }, + { 'b', "Set breakpoint on selected source/disassembly line" }, + { 'c', "Continue process" }, + { 'd', "Detach and resume process" }, + { 'D', "Detach with process suspended" }, + { 'h', "Show help dialog" }, + { 'k', "Kill process" }, + { 'n', "Step over (source line)" }, + { 'N', "Step over (single instruction)" }, + { 'o', "Step out" }, + { 's', "Step in (source line)" }, + { 'S', "Step in (single instruction)" }, + { ',', "Page up" }, + { '.', "Page down" }, + { '\0', NULL } + }; + return g_source_view_key_help; + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = NULL; + + bool update_location = false; + if (process) + { + StateType state = process->GetState(); + if (StateIsStoppedState(state, true)) + { + // We are stopped, so it is ok to + update_location = true; + } + } + + m_min_x = 1; + m_min_y = 1; + m_max_x = window.GetMaxX()-1; + m_max_y = window.GetMaxY()-1; + + const uint32_t num_visible_lines = NumVisibleLines(); + StackFrameSP frame_sp; + bool set_selected_line_to_pc = false; + + + if (update_location) + { + + const bool process_alive = process ? process->IsAlive() : false; + bool thread_changed = false; + if (process_alive) + { + thread = exe_ctx.GetThreadPtr(); + if (thread) + { + frame_sp = thread->GetSelectedFrame(); + auto tid = thread->GetID(); + thread_changed = tid != m_tid; + m_tid = tid; + } + else + { + if (m_tid != LLDB_INVALID_THREAD_ID) + { + thread_changed = true; + m_tid = LLDB_INVALID_THREAD_ID; + } + } + } + const uint32_t stop_id = process ? process->GetStopID() : 0; + const bool stop_id_changed = stop_id != m_stop_id; + bool frame_changed = false; + m_stop_id = stop_id; + if (frame_sp) + { + m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); + const uint32_t frame_idx = frame_sp->GetFrameIndex(); + frame_changed = frame_idx != m_frame_idx; + m_frame_idx = frame_idx; + } + else + { + m_sc.Clear(true); + frame_changed = m_frame_idx != UINT32_MAX; + m_frame_idx = UINT32_MAX; + } + + const bool context_changed = thread_changed || frame_changed || stop_id_changed; + + if (process_alive) + { + if (m_sc.line_entry.IsValid()) + { + m_pc_line = m_sc.line_entry.line; + if (m_pc_line != UINT32_MAX) + --m_pc_line; // Convert to zero based line number... + // Update the selected line if the stop ID changed... + if (context_changed) + m_selected_line = m_pc_line; + + if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) + { + // Same file, nothing to do, we should either have the + // lines or not (source file missing) + if (m_selected_line >= m_first_visible_line) + { + if (m_selected_line >= m_first_visible_line + num_visible_lines) + m_first_visible_line = m_selected_line - 10; + } + else + { + if (m_selected_line > 10) + m_first_visible_line = m_selected_line - 10; + else + m_first_visible_line = 0; + } + } + else + { + // File changed, set selected line to the line with the PC + m_selected_line = m_pc_line; + m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file); + if (m_file_sp) + { + const size_t num_lines = m_file_sp->GetNumLines(); + int m_line_width = 1; + for (size_t n = num_lines; n >= 10; n = n / 10) + ++m_line_width; + + snprintf (m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width); + if (num_lines < num_visible_lines || m_selected_line < num_visible_lines) + m_first_visible_line = 0; + else + m_first_visible_line = m_selected_line - 10; + } + } + } + else + { + m_file_sp.reset(); + } + + if (!m_file_sp || m_file_sp->GetNumLines() == 0) + { + // Show disassembly + bool prefer_file_cache = false; + if (m_sc.function) + { + if (m_disassembly_scope != m_sc.function) + { + m_disassembly_scope = m_sc.function; + m_disassembly_sp = m_sc.function->GetInstructions (exe_ctx, NULL, prefer_file_cache); + if (m_disassembly_sp) + { + set_selected_line_to_pc = true; + m_disassembly_range = m_sc.function->GetAddressRange(); + } + else + { + m_disassembly_range.Clear(); + } + } + else + { + set_selected_line_to_pc = context_changed; + } + } + else if (m_sc.symbol) + { + if (m_disassembly_scope != m_sc.symbol) + { + m_disassembly_scope = m_sc.symbol; + m_disassembly_sp = m_sc.symbol->GetInstructions (exe_ctx, NULL, prefer_file_cache); + if (m_disassembly_sp) + { + set_selected_line_to_pc = true; + m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress(); + m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize()); + } + else + { + m_disassembly_range.Clear(); + } + } + else + { + set_selected_line_to_pc = context_changed; + } + } + } + } + else + { + m_pc_line = UINT32_MAX; + } + } + + + window.Erase(); + window.DrawTitleBox ("Sources"); + + + Target *target = exe_ctx.GetTargetPtr(); + const size_t num_source_lines = GetNumSourceLines(); + if (num_source_lines > 0) + { + // Display source + BreakpointLines bp_lines; + if (target) + { + BreakpointList &bp_list = target->GetBreakpointList(); + const size_t num_bps = bp_list.GetSize(); + for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx) + { + BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); + const size_t num_bps_locs = bp_sp->GetNumLocations(); + for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx) + { + BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx); + LineEntry bp_loc_line_entry; + if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry (bp_loc_line_entry)) + { + if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) + { + bp_lines.insert(bp_loc_line_entry.line); + } + } + } + } + } + + + const attr_t selected_highlight_attr = A_REVERSE; + const attr_t pc_highlight_attr = COLOR_PAIR(1); + + for (int i=0; i<num_visible_lines; ++i) + { + const uint32_t curr_line = m_first_visible_line + i; + if (curr_line < num_source_lines) + { + const int line_y = 1+i; + window.MoveCursor(1, line_y); + const bool is_pc_line = curr_line == m_pc_line; + const bool line_is_selected = m_selected_line == curr_line; + // Highlight the line as the PC line first, then if the selected line + // isn't the same as the PC line, highlight it differently + attr_t highlight_attr = 0; + attr_t bp_attr = 0; + if (is_pc_line) + highlight_attr = pc_highlight_attr; + else if (line_is_selected) + highlight_attr = selected_highlight_attr; + + if (bp_lines.find(curr_line+1) != bp_lines.end()) + bp_attr = COLOR_PAIR(2); + + if (bp_attr) + window.AttributeOn(bp_attr); + + window.Printf (m_line_format, curr_line + 1); + + if (bp_attr) + window.AttributeOff(bp_attr); + + window.PutChar(ACS_VLINE); + // Mark the line with the PC with a diamond + if (is_pc_line) + window.PutChar(ACS_DIAMOND); + else + window.PutChar(' '); + + if (highlight_attr) + window.AttributeOn(highlight_attr); + const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false); + if (line_len > 0) + window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len); + + if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0) + { + StopInfoSP stop_info_sp; + if (thread) + stop_info_sp = thread->GetStopInfo(); + if (stop_info_sp) + { + const char *stop_description = stop_info_sp->GetDescription(); + if (stop_description && stop_description[0]) + { + size_t stop_description_len = strlen(stop_description); + int desc_x = window.GetWidth() - stop_description_len - 16; + window.Printf ("%*s", desc_x - window.GetCursorX(), ""); + //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y); + window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description); + } + } + else + { + window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, ""); + } + } + if (highlight_attr) + window.AttributeOff(highlight_attr); + + } + else + { + break; + } + } + } + else + { + size_t num_disassembly_lines = GetNumDisassemblyLines(); + if (num_disassembly_lines > 0) + { + // Display disassembly + BreakpointAddrs bp_file_addrs; + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + BreakpointList &bp_list = target->GetBreakpointList(); + const size_t num_bps = bp_list.GetSize(); + for (size_t bp_idx=0; bp_idx<num_bps; ++bp_idx) + { + BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); + const size_t num_bps_locs = bp_sp->GetNumLocations(); + for (size_t bp_loc_idx=0; bp_loc_idx<num_bps_locs; ++bp_loc_idx) + { + BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx); + LineEntry bp_loc_line_entry; + const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress(); + if (file_addr != LLDB_INVALID_ADDRESS) + { + if (m_disassembly_range.ContainsFileAddress(file_addr)) + bp_file_addrs.insert(file_addr); + } + } + } + } + + + const attr_t selected_highlight_attr = A_REVERSE; + const attr_t pc_highlight_attr = COLOR_PAIR(1); + + StreamString strm; + + InstructionList &insts = m_disassembly_sp->GetInstructionList(); + Address pc_address; + + if (frame_sp) + pc_address = frame_sp->GetFrameCodeAddress(); + const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress (pc_address) : UINT32_MAX; + if (set_selected_line_to_pc) + { + m_selected_line = pc_idx; + } + + const uint32_t non_visible_pc_offset = (num_visible_lines / 5); + if (m_first_visible_line >= num_disassembly_lines) + m_first_visible_line = 0; + + if (pc_idx < num_disassembly_lines) + { + if (pc_idx < m_first_visible_line || + pc_idx >= m_first_visible_line + num_visible_lines) + m_first_visible_line = pc_idx - non_visible_pc_offset; + } + + for (size_t i=0; i<num_visible_lines; ++i) + { + const uint32_t inst_idx = m_first_visible_line + i; + Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get(); + if (!inst) + break; + + window.MoveCursor(1, i+1); + const bool is_pc_line = frame_sp && inst_idx == pc_idx; + const bool line_is_selected = m_selected_line == inst_idx; + // Highlight the line as the PC line first, then if the selected line + // isn't the same as the PC line, highlight it differently + attr_t highlight_attr = 0; + attr_t bp_attr = 0; + if (is_pc_line) + highlight_attr = pc_highlight_attr; + else if (line_is_selected) + highlight_attr = selected_highlight_attr; + + if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end()) + bp_attr = COLOR_PAIR(2); + + if (bp_attr) + window.AttributeOn(bp_attr); + + window.Printf (" 0x%16.16llx ", inst->GetAddress().GetLoadAddress(target)); + + if (bp_attr) + window.AttributeOff(bp_attr); + + window.PutChar(ACS_VLINE); + // Mark the line with the PC with a diamond + if (is_pc_line) + window.PutChar(ACS_DIAMOND); + else + window.PutChar(' '); + + if (highlight_attr) + window.AttributeOn(highlight_attr); + + const char *mnemonic = inst->GetMnemonic(&exe_ctx); + const char *operands = inst->GetOperands(&exe_ctx); + const char *comment = inst->GetComment(&exe_ctx); + + if (mnemonic && mnemonic[0] == '\0') + mnemonic = NULL; + if (operands && operands[0] == '\0') + operands = NULL; + if (comment && comment[0] == '\0') + comment = NULL; + + strm.Clear(); + + if (mnemonic && operands && comment) + strm.Printf ("%-8s %-25s ; %s", mnemonic, operands, comment); + else if (mnemonic && operands) + strm.Printf ("%-8s %s", mnemonic, operands); + else if (mnemonic) + strm.Printf ("%s", mnemonic); + + int right_pad = 1; + window.PutCStringTruncated(strm.GetString().c_str(), right_pad); + + if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0) + { + StopInfoSP stop_info_sp; + if (thread) + stop_info_sp = thread->GetStopInfo(); + if (stop_info_sp) + { + const char *stop_description = stop_info_sp->GetDescription(); + if (stop_description && stop_description[0]) + { + size_t stop_description_len = strlen(stop_description); + int desc_x = window.GetWidth() - stop_description_len - 16; + window.Printf ("%*s", desc_x - window.GetCursorX(), ""); + //window.MoveCursor(window.GetWidth() - stop_description_len - 15, line_y); + window.Printf ("<<< Thread %u: %s ", thread->GetIndexID(), stop_description); + } + } + else + { + window.Printf ("%*s", window.GetWidth() - window.GetCursorX() - 1, ""); + } + } + if (highlight_attr) + window.AttributeOff(highlight_attr); + } + } + } + window.DeferredRefresh(); + return true; // Drawing handled + } + + size_t + GetNumLines () + { + size_t num_lines = GetNumSourceLines(); + if (num_lines == 0) + num_lines = GetNumDisassemblyLines(); + return num_lines; + } + + size_t + GetNumSourceLines () const + { + if (m_file_sp) + return m_file_sp->GetNumLines(); + return 0; + } + size_t + GetNumDisassemblyLines () const + { + if (m_disassembly_sp) + return m_disassembly_sp->GetInstructionList().GetSize(); + return 0; + } + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int c) + { + const uint32_t num_visible_lines = NumVisibleLines(); + const size_t num_lines = GetNumLines (); + + switch (c) + { + case ',': + case KEY_PPAGE: + // Page up key + if (m_first_visible_line > num_visible_lines) + m_first_visible_line -= num_visible_lines; + else + m_first_visible_line = 0; + m_selected_line = m_first_visible_line; + return eKeyHandled; + + case '.': + case KEY_NPAGE: + // Page down key + { + if (m_first_visible_line + num_visible_lines < num_lines) + m_first_visible_line += num_visible_lines; + else if (num_lines < num_visible_lines) + m_first_visible_line = 0; + else + m_first_visible_line = num_lines - num_visible_lines; + m_selected_line = m_first_visible_line; + } + return eKeyHandled; + + case KEY_UP: + if (m_selected_line > 0) + { + m_selected_line--; + if (m_first_visible_line > m_selected_line) + m_first_visible_line = m_selected_line; + } + return eKeyHandled; + + case KEY_DOWN: + if (m_selected_line + 1 < num_lines) + { + m_selected_line++; + if (m_first_visible_line + num_visible_lines < m_selected_line) + m_first_visible_line++; + } + return eKeyHandled; + + case '\r': + case '\n': + case KEY_ENTER: + // Set a breakpoint and run to the line using a one shot breakpoint + if (GetNumSourceLines() > 0) + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) + { + BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules + m_file_sp->GetFileSpec(), // Source file + m_selected_line + 1, // Source line number (m_selected_line is zero based) + eLazyBoolCalculate, // Check inlines using global setting + eLazyBoolCalculate, // Skip prologue using global setting, + false, // internal + false); // request_hardware + // Make breakpoint one shot + bp_sp->GetOptions()->SetOneShot(true); + exe_ctx.GetProcessRef().Resume(); + } + } + else if (m_selected_line < GetNumDisassemblyLines()) + { + const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get(); + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasTargetScope()) + { + Address addr = inst->GetAddress(); + BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address + false, // internal + false); // request_hardware + // Make breakpoint one shot + bp_sp->GetOptions()->SetOneShot(true); + exe_ctx.GetProcessRef().Resume(); + } + } + return eKeyHandled; + + case 'b': // 'b' == toggle breakpoint on currently selected line + if (m_selected_line < GetNumSourceLines()) + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasTargetScope()) + { + BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (NULL, // Don't limit the breakpoint to certain modules + m_file_sp->GetFileSpec(), // Source file + m_selected_line + 1, // Source line number (m_selected_line is zero based) + eLazyBoolCalculate, // Check inlines using global setting + eLazyBoolCalculate, // Skip prologue using global setting, + false, // internal + false); // request_hardware + } + } + else if (m_selected_line < GetNumDisassemblyLines()) + { + const Instruction *inst = m_disassembly_sp->GetInstructionList().GetInstructionAtIndex(m_selected_line).get(); + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasTargetScope()) + { + Address addr = inst->GetAddress(); + BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint (addr, // lldb_private::Address + false, // internal + false); // request_hardware + } + } + return eKeyHandled; + + case 'd': // 'd' == detach and let run + case 'D': // 'D' == detach and keep stopped + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + exe_ctx.GetProcessRef().Detach(c == 'D'); + } + return eKeyHandled; + + case 'k': + // 'k' == kill + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + exe_ctx.GetProcessRef().Destroy(); + } + return eKeyHandled; + + case 'c': + // 'c' == continue + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasProcessScope()) + exe_ctx.GetProcessRef().Resume(); + } + return eKeyHandled; + + case 'o': + // 'o' == step out + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true)) + { + exe_ctx.GetThreadRef().StepOut(); + } + } + return eKeyHandled; + case 'n': // 'n' == step over + case 'N': // 'N' == step over instruction + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true)) + { + bool source_step = (c == 'n'); + exe_ctx.GetThreadRef().StepOver(source_step); + } + } + return eKeyHandled; + case 's': // 's' == step into + case 'S': // 'S' == step into instruction + { + ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); + if (exe_ctx.HasThreadScope() && StateIsStoppedState (exe_ctx.GetProcessRef().GetState(), true)) + { + bool source_step = (c == 's'); + bool avoid_code_without_debug_info = true; + exe_ctx.GetThreadRef().StepIn(source_step, avoid_code_without_debug_info); + } + } + return eKeyHandled; + + case 'h': + window.CreateHelpSubwindow (); + return eKeyHandled; + + default: + break; + } + return eKeyNotHandled; + } + +protected: + typedef std::set<uint32_t> BreakpointLines; + typedef std::set<lldb::addr_t> BreakpointAddrs; + + Debugger &m_debugger; + SymbolContext m_sc; + SourceManager::FileSP m_file_sp; + SymbolContextScope *m_disassembly_scope; + lldb::DisassemblerSP m_disassembly_sp; + AddressRange m_disassembly_range; + lldb::user_id_t m_tid; + char m_line_format[8]; + int m_line_width; + uint32_t m_selected_line; // The selected line + uint32_t m_pc_line; // The line with the PC + uint32_t m_stop_id; + uint32_t m_frame_idx; + int m_first_visible_line; + int m_min_x; + int m_min_y; + int m_max_x; + int m_max_y; + +}; + +DisplayOptions ValueObjectListDelegate::g_options = { true }; + +IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) : + IOHandler (debugger) +{ +} + +void +IOHandlerCursesGUI::Activate () +{ + IOHandler::Activate(); + if (!m_app_ap) + { + m_app_ap.reset (new Application (GetInputFILE(), GetOutputFILE())); + + + // This is both a window and a menu delegate + std::shared_ptr<ApplicationDelegate> app_delegate_sp(new ApplicationDelegate(*m_app_ap, m_debugger)); + + MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast<MenuDelegate>(app_delegate_sp); + MenuSP lldb_menu_sp(new Menu("LLDB" , "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB)); + MenuSP exit_menuitem_sp(new Menu("Exit", NULL, 'x', ApplicationDelegate::eMenuID_LLDBExit)); + exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit); + lldb_menu_sp->AddSubmenu (MenuSP (new Menu("About LLDB", NULL, 'a', ApplicationDelegate::eMenuID_LLDBAbout))); + lldb_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator))); + lldb_menu_sp->AddSubmenu (exit_menuitem_sp); + + MenuSP target_menu_sp(new Menu("Target" ,"F2", KEY_F(2), ApplicationDelegate::eMenuID_Target)); + target_menu_sp->AddSubmenu (MenuSP (new Menu("Create", NULL, 'c', ApplicationDelegate::eMenuID_TargetCreate))); + target_menu_sp->AddSubmenu (MenuSP (new Menu("Delete", NULL, 'd', ApplicationDelegate::eMenuID_TargetDelete))); + + MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process)); + process_menu_sp->AddSubmenu (MenuSP (new Menu("Attach" , NULL, 'a', ApplicationDelegate::eMenuID_ProcessAttach))); + process_menu_sp->AddSubmenu (MenuSP (new Menu("Detach" , NULL, 'd', ApplicationDelegate::eMenuID_ProcessDetach))); + process_menu_sp->AddSubmenu (MenuSP (new Menu("Launch" , NULL, 'l', ApplicationDelegate::eMenuID_ProcessLaunch))); + process_menu_sp->AddSubmenu (MenuSP (new Menu(Menu::Type::Separator))); + process_menu_sp->AddSubmenu (MenuSP (new Menu("Continue", NULL, 'c', ApplicationDelegate::eMenuID_ProcessContinue))); + process_menu_sp->AddSubmenu (MenuSP (new Menu("Halt" , NULL, 'h', ApplicationDelegate::eMenuID_ProcessHalt))); + process_menu_sp->AddSubmenu (MenuSP (new Menu("Kill" , NULL, 'k', ApplicationDelegate::eMenuID_ProcessKill))); + + MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread)); + thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step In" , NULL, 'i', ApplicationDelegate::eMenuID_ThreadStepIn))); + thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Over", NULL, 'v', ApplicationDelegate::eMenuID_ThreadStepOver))); + thread_menu_sp->AddSubmenu (MenuSP (new Menu("Step Out" , NULL, 'o', ApplicationDelegate::eMenuID_ThreadStepOut))); + + MenuSP view_menu_sp(new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View)); + view_menu_sp->AddSubmenu (MenuSP (new Menu("Backtrace", NULL, 'b', ApplicationDelegate::eMenuID_ViewBacktrace))); + view_menu_sp->AddSubmenu (MenuSP (new Menu("Registers", NULL, 'r', ApplicationDelegate::eMenuID_ViewRegisters))); + view_menu_sp->AddSubmenu (MenuSP (new Menu("Source" , NULL, 's', ApplicationDelegate::eMenuID_ViewSource))); + view_menu_sp->AddSubmenu (MenuSP (new Menu("Variables", NULL, 'v', ApplicationDelegate::eMenuID_ViewVariables))); + + MenuSP help_menu_sp(new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help)); + help_menu_sp->AddSubmenu (MenuSP (new Menu("GUI Help", NULL, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp))); + + m_app_ap->Initialize(); + WindowSP &main_window_sp = m_app_ap->GetMainWindow(); + + MenuSP menubar_sp(new Menu(Menu::Type::Bar)); + menubar_sp->AddSubmenu (lldb_menu_sp); + menubar_sp->AddSubmenu (target_menu_sp); + menubar_sp->AddSubmenu (process_menu_sp); + menubar_sp->AddSubmenu (thread_menu_sp); + menubar_sp->AddSubmenu (view_menu_sp); + menubar_sp->AddSubmenu (help_menu_sp); + menubar_sp->SetDelegate(app_menu_delegate_sp); + + Rect content_bounds = main_window_sp->GetFrame(); + Rect menubar_bounds = content_bounds.MakeMenuBar(); + Rect status_bounds = content_bounds.MakeStatusBar(); + Rect source_bounds; + Rect variables_bounds; + Rect threads_bounds; + Rect source_variables_bounds; + content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds); + source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds); + + WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false); + // Let the menubar get keys if the active window doesn't handle the + // keys that are typed so it can respond to menubar key presses. + menubar_window_sp->SetCanBeActive(false); // Don't let the menubar become the active window + menubar_window_sp->SetDelegate(menubar_sp); + + WindowSP source_window_sp (main_window_sp->CreateSubWindow("Source", + source_bounds, + true)); + WindowSP variables_window_sp (main_window_sp->CreateSubWindow("Variables", + variables_bounds, + false)); + WindowSP threads_window_sp (main_window_sp->CreateSubWindow("Threads", + threads_bounds, + false)); + WindowSP status_window_sp (main_window_sp->CreateSubWindow("Status", + status_bounds, + false)); + status_window_sp->SetCanBeActive(false); // Don't let the status bar become the active window + main_window_sp->SetDelegate (std::static_pointer_cast<WindowDelegate>(app_delegate_sp)); + source_window_sp->SetDelegate (WindowDelegateSP(new SourceFileWindowDelegate(m_debugger))); + variables_window_sp->SetDelegate (WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); + TreeDelegateSP thread_delegate_sp (new ThreadTreeDelegate(m_debugger)); + threads_window_sp->SetDelegate (WindowDelegateSP(new TreeWindowDelegate(m_debugger, thread_delegate_sp))); + status_window_sp->SetDelegate (WindowDelegateSP(new StatusBarWindowDelegate(m_debugger))); + + // Show the main help window once the first time the curses GUI is launched + static bool g_showed_help = false; + if (!g_showed_help) + { + g_showed_help = true; + main_window_sp->CreateHelpSubwindow(); + } + + init_pair (1, COLOR_WHITE , COLOR_BLUE ); + init_pair (2, COLOR_BLACK , COLOR_WHITE ); + init_pair (3, COLOR_MAGENTA , COLOR_WHITE ); + init_pair (4, COLOR_MAGENTA , COLOR_BLACK ); + init_pair (5, COLOR_RED , COLOR_BLACK ); + + } +} + +void +IOHandlerCursesGUI::Deactivate () +{ + m_app_ap->Terminate(); +} + +void +IOHandlerCursesGUI::Run () +{ + m_app_ap->Run(m_debugger); + SetIsDone(true); +} + + +IOHandlerCursesGUI::~IOHandlerCursesGUI () +{ + +} + +void +IOHandlerCursesGUI::Hide () +{ +} + + +void +IOHandlerCursesGUI::Refresh () +{ +} + + +void +IOHandlerCursesGUI::Interrupt () +{ +} + + +void +IOHandlerCursesGUI::GotEOF() +{ +} + +#endif // #ifndef LLDB_DISABLE_CURSES
\ No newline at end of file diff --git a/source/Core/InputReader.cpp b/source/Core/InputReader.cpp deleted file mode 100644 index cbaa671bcba5..000000000000 --- a/source/Core/InputReader.cpp +++ /dev/null @@ -1,387 +0,0 @@ -//===-- InputReader.cpp -----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/lldb-python.h" - -#include <string> - -#include "lldb/Core/InputReader.h" -#include "lldb/Core/Debugger.h" -#include "lldb/Interpreter/CommandInterpreter.h" - -using namespace lldb; -using namespace lldb_private; - -InputReader::InputReader (Debugger &debugger) : - m_debugger (debugger), - m_callback (NULL), - m_callback_baton (NULL), - m_end_token (), - m_granularity (eInputReaderGranularityInvalid), - m_done (true), - m_echo (true), - m_active (false), - m_reader_done (false), - m_user_input(), - m_save_user_input(false) -{ -} - -InputReader::~InputReader () -{ -} - -Error -InputReader::Initialize -( - Callback callback, - void *baton, - lldb::InputReaderGranularity granularity, - const char *end_token, - const char *prompt, - bool echo -) -{ - Error err; - m_callback = callback; - m_callback_baton = baton, - m_granularity = granularity; - if (end_token != NULL) - m_end_token = end_token; - if (prompt != NULL) - m_prompt = prompt; - m_done = true; - m_echo = echo; - - if (m_granularity == eInputReaderGranularityInvalid) - { - err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'."); - } - else - if (end_token != NULL && granularity != eInputReaderGranularityInvalid) - { - if (granularity == eInputReaderGranularityByte) - { - // Check to see if end_token is longer than one byte. - - if (strlen (end_token) > 1) - { - err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte)."); - } - } - else if (granularity == eInputReaderGranularityWord) - { - // Check to see if m_end_token contains any white space (i.e. is multiple words). - - const char *white_space = " \t\n"; - size_t pos = m_end_token.find_first_of (white_space); - if (pos != std::string::npos) - { - err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word)."); - } - } - else - { - // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens. - - size_t pos = m_end_token.find_first_of ('\n'); - if (pos != std::string::npos) - { - err.SetErrorString ("Invalid end token: End token cannot contain a newline."); - } - } - } - - m_done = err.Fail(); - - return err; -} - -size_t -InputReader::HandleRawBytes (const char *bytes, size_t bytes_len) -{ - const char *end_token = NULL; - - if (m_end_token.empty() == false) - { - end_token = ::strstr (bytes, m_end_token.c_str()); - if (end_token >= bytes + bytes_len) - end_token = NULL; - } - - const char *p = bytes; - const char *end = bytes + bytes_len; - - switch (m_granularity) - { - case eInputReaderGranularityInvalid: - break; - - case eInputReaderGranularityByte: - while (p < end) - { - if (end_token == p) - { - p += m_end_token.size(); - SetIsDone(true); - break; - } - - if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0) - break; - ++p; - if (IsDone()) - break; - } - // Return how many bytes were handled. - return p - bytes; - break; - - - case eInputReaderGranularityWord: - { - char quote = '\0'; - const char *word_start = NULL; - bool send_word = false; - for (; p < end; ++p, send_word = false) - { - if (end_token && end_token == p) - { - m_end_token.size(); - SetIsDone(true); - break; - } - - const char ch = *p; - if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\'))) - { - // We have a space character or the terminating quote - send_word = word_start != NULL; - quote = '\0'; - } - else if (quote) - { - // We are in the middle of a quoted character - continue; - } - else if (ch == '"' || ch == '\'' || ch == '`') - quote = ch; - else if (word_start == NULL) - { - // We have the first character in a word - word_start = p; - } - - if (send_word) - { - const size_t word_len = p - word_start; - size_t bytes_handled = m_callback (m_callback_baton, - *this, - eInputReaderGotToken, - word_start, - word_len); - - if (bytes_handled != word_len) - return word_start - bytes + bytes_handled; - - if (IsDone()) - return p - bytes; - } - } - } - break; - - - case eInputReaderGranularityLine: - { - const char *line_start = bytes; - const char *end_line = NULL; - while (p < end) - { - const char ch = *p; - if (ch == '\n' || ch == '\r') - { - size_t line_length = p - line_start; - // Now skip the newline character - ++p; - // Skip a complete DOS newline if we run into one - if (ch == 0xd && p < end && *p == 0xa) - ++p; - - if (line_start <= end_token && end_token < line_start + line_length) - { - SetIsDone(true); - m_callback (m_callback_baton, - *this, - eInputReaderGotToken, - line_start, - end_token - line_start); - - return p - bytes; - } - - size_t bytes_handled = m_callback (m_callback_baton, - *this, - eInputReaderGotToken, - line_start, - line_length); - - end_line = p; - - if (bytes_handled != line_length) - { - // The input reader wasn't able to handle all the data - return line_start - bytes + bytes_handled; - } - - - if (IsDone()) - return p - bytes; - - line_start = p; - } - else - { - ++p; - } - } - - if (end_line) - return end_line - bytes; - } - break; - - - case eInputReaderGranularityAll: - { - // Nothing should be handle unless we see our end token - if (end_token) - { - size_t length = end_token - bytes; - size_t bytes_handled = m_callback (m_callback_baton, - *this, - eInputReaderGotToken, - bytes, - length); - m_done = true; - - p += bytes_handled + m_end_token.size(); - - // Consume any white space, such as newlines, beyond the end token - - while (p < end && isspace(*p)) - ++p; - - if (bytes_handled != length) - return bytes_handled; - else - { - return p - bytes; - //return bytes_handled + m_end_token.size(); - } - } - return 0; - } - break; - } - return 0; -} - -const char * -InputReader::GetPrompt () const -{ - if (!m_prompt.empty()) - return m_prompt.c_str(); - else - return NULL; -} - -void -InputReader::RefreshPrompt () -{ - if (m_debugger.GetCommandInterpreter().GetBatchCommandMode()) - return; - - if (!m_prompt.empty()) - { - File &out_file = m_debugger.GetOutputFile(); - if (out_file.IsValid()) - { - out_file.Printf ("%s", m_prompt.c_str()); - out_file.Flush(); - } - } -} - -void -InputReader::Notify (InputReaderAction notification) -{ - switch (notification) - { - case eInputReaderActivate: - case eInputReaderReactivate: - m_active = true; - m_reader_done.SetValue(false, eBroadcastAlways); - break; - - case eInputReaderDeactivate: - case eInputReaderDone: - m_active = false; - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderInterrupt: - case eInputReaderEndOfFile: - break; - - case eInputReaderGotToken: - return; // We don't notify the tokens here, it is done in HandleRawBytes - } - if (m_callback) - m_callback (m_callback_baton, *this, notification, NULL, 0); - if (notification == eInputReaderDone) - m_reader_done.SetValue(true, eBroadcastAlways); -} - -void -InputReader::WaitOnReaderIsDone () -{ - m_reader_done.WaitForValueEqualTo (true); -} - -const char * -InputReader::GranularityAsCString (lldb::InputReaderGranularity granularity) -{ - switch (granularity) - { - case eInputReaderGranularityInvalid: return "invalid"; - case eInputReaderGranularityByte: return "byte"; - case eInputReaderGranularityWord: return "word"; - case eInputReaderGranularityLine: return "line"; - case eInputReaderGranularityAll: return "all"; - } - - static char unknown_state_string[64]; - snprintf(unknown_state_string, sizeof (unknown_state_string), "InputReaderGranularity = %i", granularity); - return unknown_state_string; -} - -bool -InputReader::HandlerData::GetBatchMode() -{ - return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); -} - -lldb::StreamSP -InputReader::HandlerData::GetOutStream() -{ - return reader.GetDebugger().GetAsyncOutputStream(); -} diff --git a/source/Core/InputReaderEZ.cpp b/source/Core/InputReaderEZ.cpp deleted file mode 100644 index 7a865bdc5097..000000000000 --- a/source/Core/InputReaderEZ.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//===-- InputReaderEZ.cpp ---------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Core/InputReaderEZ.h" - -using namespace lldb; -using namespace lldb_private; - -size_t -InputReaderEZ::Callback_Impl(void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - -{ - HandlerData hand_data(reader, - bytes, - bytes_len, - baton); - - switch (notification) - { - case eInputReaderActivate: - reader.ActivateHandler(hand_data); - break; - case eInputReaderDeactivate: - reader.DeactivateHandler(hand_data); - break; - case eInputReaderReactivate: - reader.ReactivateHandler(hand_data); - break; - case eInputReaderAsynchronousOutputWritten: - reader.AsynchronousOutputWrittenHandler(hand_data); - break; - case eInputReaderGotToken: - { - if (reader.GetSaveUserInput()) - reader.GetUserInput().AppendString(bytes, bytes_len); - reader.GotTokenHandler(hand_data); - } - break; - case eInputReaderInterrupt: - reader.InterruptHandler(hand_data); - break; - case eInputReaderEndOfFile: - reader.EOFHandler(hand_data); - break; - case eInputReaderDone: - reader.DoneHandler(hand_data); - break; - } - return bytes_len; -} - -Error -InputReaderEZ::Initialize(void* baton, - lldb::InputReaderGranularity token_size, - const char* end_token, - const char *prompt, - bool echo) -{ - return InputReader::Initialize(Callback_Impl, - baton, - token_size, - end_token, - prompt, - echo); -} - -Error -InputReaderEZ::Initialize(InitializationParameters& params) -{ - Error ret = Initialize(params.m_baton, - params.m_token_size, - params.m_end_token, - params.m_prompt, - params.m_echo); - m_save_user_input = params.m_save_user_input; - return ret; -} - -InputReaderEZ::~InputReaderEZ () -{ -} diff --git a/source/Core/InputReaderStack.cpp b/source/Core/InputReaderStack.cpp deleted file mode 100644 index 764ea26550f7..000000000000 --- a/source/Core/InputReaderStack.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//===-- InputReaderStack.cpp ------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Core/InputReaderStack.h" - -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes - - -using namespace lldb; -using namespace lldb_private; - -InputReaderStack::InputReaderStack () : - m_input_readers (), - m_input_readers_mutex (Mutex::eMutexTypeRecursive) -{ -} - -InputReaderStack::~InputReaderStack () -{ -} - -size_t -InputReaderStack::GetSize () const -{ - Mutex::Locker locker (m_input_readers_mutex); - return m_input_readers.size(); -} - -void -InputReaderStack::Push (const lldb::InputReaderSP& reader_sp) -{ - if (reader_sp) - { - Mutex::Locker locker (m_input_readers_mutex); - m_input_readers.push (reader_sp); - } -} - -bool -InputReaderStack::IsEmpty () const -{ - Mutex::Locker locker (m_input_readers_mutex); - return m_input_readers.empty(); -} - -InputReaderSP -InputReaderStack::Top () -{ - InputReaderSP input_reader_sp; - { - Mutex::Locker locker (m_input_readers_mutex); - if (!m_input_readers.empty()) - input_reader_sp = m_input_readers.top(); - } - - return input_reader_sp; -} - -void -InputReaderStack::Pop () -{ - Mutex::Locker locker (m_input_readers_mutex); - if (!m_input_readers.empty()) - m_input_readers.pop(); -} - -Mutex & -InputReaderStack::GetStackMutex () -{ - return m_input_readers_mutex; -} diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp index 8d659cfd7522..b7dc056af6ce 100644 --- a/source/Core/Log.cpp +++ b/source/Core/Log.cpp @@ -83,7 +83,10 @@ Log::GetMask() const void Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) { - if (m_stream_sp) + // Make a copy of our stream shared pointer in case someone disables our + // log while we are logging and releases the stream + StreamSP stream_sp(m_stream_sp); + if (stream_sp) { static uint32_t g_sequence_id = 0; StreamString header; @@ -116,11 +119,11 @@ Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) } header.PrintfVarArg (format, args); - m_stream_sp->Printf("%s\n", header.GetData()); + stream_sp->Printf("%s\n", header.GetData()); if (m_options.Test (LLDB_LOG_OPTION_BACKTRACE)) - Host::Backtrace (*m_stream_sp, 1024); - m_stream_sp->Flush(); + Host::Backtrace (*stream_sp, 1024); + stream_sp->Flush(); } } @@ -467,8 +470,11 @@ Log::GetVerbose() const if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) return true; - if (m_stream_sp) - return m_stream_sp->GetVerbose(); + // Make a copy of our stream shared pointer in case someone disables our + // log while we are logging and releases the stream + StreamSP stream_sp(m_stream_sp); + if (stream_sp) + return stream_sp->GetVerbose(); return false; } @@ -478,8 +484,11 @@ Log::GetVerbose() const bool Log::GetDebug() const { - if (m_stream_sp) - return m_stream_sp->GetDebug(); + // Make a copy of our stream shared pointer in case someone disables our + // log while we are logging and releases the stream + StreamSP stream_sp(m_stream_sp); + if (stream_sp) + return stream_sp->GetDebug(); return false; } diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp index a41986de5143..55bd3cbb8e99 100644 --- a/source/Core/Mangled.cpp +++ b/source/Core/Mangled.cpp @@ -20,17 +20,10 @@ #ifdef LLDB_USE_BUILTIN_DEMANGLER -#include <vector> -#include <algorithm> -#include <string> -#include <numeric> -#include <cstdlib> -#include <cstring> -#include <cctype> //---------------------------------------------------------------------- // Inlined copy of: // http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp -// revision 193704. +// revision 199944. // // Changes include: // - remove the "__cxxabiv1" namespace @@ -38,8 +31,32 @@ // - removed extern "C" from the cxa_demangle function // - Changed the scope of the unnamed namespace to include cxa_demangle // function. +// - Added "#undef _LIBCPP_EXTERN_TEMPLATE" to avoid warning //---------------------------------------------------------------------- +#undef _LIBCPP_EXTERN_TEMPLATE // Avoid warning below + +//===-------------------------- cxa_demangle.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LIBCPP_EXTERN_TEMPLATE(...) +#define _LIBCPP_NO_EXCEPTIONS + +#include <vector> +#include <algorithm> +#include <string> +#include <numeric> +#include <cstdlib> +#include <cstring> +#include <cctype> + + namespace { @@ -558,6 +575,8 @@ parse_template_param(const char* first, const char* last, C& db) { if (first[1] == '_') { + if (db.template_param.empty()) + return first; if (!db.template_param.back().empty()) { for (auto& t : db.template_param.back().front()) @@ -580,7 +599,7 @@ parse_template_param(const char* first, const char* last, C& db) sub *= 10; sub += static_cast<size_t>(*t - '0'); } - if (t == last || *t != '_') + if (t == last || *t != '_' || db.template_param.empty()) return first; ++sub; if (sub < db.template_param.back().size()) @@ -615,6 +634,8 @@ parse_const_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto expr = db.names.back().move_full(); db.names.pop_back(); db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; @@ -639,6 +660,8 @@ parse_dynamic_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto expr = db.names.back().move_full(); db.names.pop_back(); db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; @@ -663,6 +686,8 @@ parse_reinterpret_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto expr = db.names.back().move_full(); db.names.pop_back(); db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; @@ -687,6 +712,8 @@ parse_static_cast_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto expr = db.names.back().move_full(); db.names.pop_back(); db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")"; @@ -723,6 +750,8 @@ parse_sizeof_type_expr(const char* first, const char* last, C& db) const char* t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; first = t; } @@ -741,6 +770,8 @@ parse_sizeof_expr_expr(const char* first, const char* last, C& db) const char* t = parse_expression(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; first = t; } @@ -832,6 +863,8 @@ parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db const char* t = parse_function_param(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; first = t; } @@ -855,6 +888,8 @@ parse_typeid_expr(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back() = "typeid(" + db.names.back().move_full() + ")"; first = t; } @@ -873,6 +908,8 @@ parse_throw_expr(const char* first, const char* last, C& db) const char* t = parse_expression(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back() = "throw " + db.names.back().move_full(); first = t; } @@ -894,6 +931,8 @@ parse_dot_star_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto expr = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += ".*" + expr; @@ -918,6 +957,8 @@ parse_simple_id(const char* first, const char* last, C& db) const char* t1 = parse_template_args(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto args = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += std::move(args); @@ -964,6 +1005,8 @@ parse_unresolved_type(const char* first, const char* last, C& db) t = parse_decltype(first, last, db); if (t != first) { + if (db.names.empty()) + return first; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); first = t; } @@ -979,6 +1022,8 @@ parse_unresolved_type(const char* first, const char* last, C& db) t = parse_unqualified_name(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "std::"); db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); first = t; @@ -1005,6 +1050,8 @@ parse_destructor_name(const char* first, const char* last, C& db) t = parse_simple_id(first, last, db); if (t != first) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "~"); first = t; } @@ -1036,6 +1083,8 @@ parse_base_unresolved_name(const char* first, const char* last, C& db) first = parse_template_args(t, last, db); if (first != t) { + if (db.names.size() < 2) + return first; auto args = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += std::move(args); @@ -1060,6 +1109,8 @@ parse_base_unresolved_name(const char* first, const char* last, C& db) first = parse_template_args(t, last, db); if (first != t) { + if (db.names.size() < 2) + return first; auto args = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += std::move(args); @@ -1109,7 +1160,11 @@ parse_unresolved_name(const char* first, const char* last, C& db) if (t2 != t) { if (global) + { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "::"); + } first = t2; } else if (last - t > 2 && t[0] == 's' && t[1] == 'r') @@ -1124,6 +1179,8 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_template_args(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto args = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += std::move(args); @@ -1137,7 +1194,7 @@ parse_unresolved_name(const char* first, const char* last, C& db) while (*t != 'E') { t1 = parse_unresolved_qualifier_level(t, last, db); - if (t1 == t || t1 == last) + if (t1 == t || t1 == last || db.names.size() < 2) return first; auto s = db.names.back().move_full(); db.names.pop_back(); @@ -1148,9 +1205,12 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_base_unresolved_name(t, last, db); if (t1 == t) { - db.names.pop_back(); + if (!db.names.empty()) + db.names.pop_back(); return first; } + if (db.names.size() < 2) + return first; auto s = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += "::" + std::move(s); @@ -1166,6 +1226,8 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_template_args(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto args = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += std::move(args); @@ -1174,9 +1236,12 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_base_unresolved_name(t, last, db); if (t1 == t) { - db.names.pop_back(); + if (!db.names.empty()) + db.names.pop_back(); return first; } + if (db.names.size() < 2) + return first; auto s = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += "::" + std::move(s); @@ -1189,11 +1254,15 @@ parse_unresolved_name(const char* first, const char* last, C& db) return first; t = t1; if (global) + { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "::"); + } while (*t != 'E') { t1 = parse_unresolved_qualifier_level(t, last, db); - if (t1 == t || t1 == last) + if (t1 == t || t1 == last || db.names.size() < 2) return first; auto s = db.names.back().move_full(); db.names.pop_back(); @@ -1204,9 +1273,12 @@ parse_unresolved_name(const char* first, const char* last, C& db) t1 = parse_base_unresolved_name(t, last, db); if (t1 == t) { - db.names.pop_back(); + if (!db.names.empty()) + db.names.pop_back(); return first; } + if (db.names.size() < 2) + return first; auto s = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += "::" + std::move(s); @@ -1232,6 +1304,8 @@ parse_dot_expr(const char* first, const char* last, C& db) const char* t1 = parse_unresolved_name(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto name = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += "." + name; @@ -1255,6 +1329,8 @@ parse_call_expr(const char* first, const char* last, C& db) { if (t == last) return first; + if (db.names.empty()) + return first; db.names.back().first += db.names.back().second; db.names.back().second = typename C::String(); db.names.back().first.append("("); @@ -1264,10 +1340,14 @@ parse_call_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 == t || t1 == last) return first; + if (db.names.empty()) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); if (!tmp.empty()) { + if (db.names.empty()) + return first; if (!first_expr) { db.names.back().first.append(", "); @@ -1278,6 +1358,8 @@ parse_call_expr(const char* first, const char* last, C& db) t = t1; } ++t; + if (db.names.empty()) + return first; db.names.back().first.append(")"); first = t; } @@ -1320,10 +1402,14 @@ parse_new_expr(const char* first, const char* last, C& db) has_expr_list = true; if (!first_expr) { + if (db.names.empty()) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); if (!tmp.empty()) { + if (db.names.empty()) + return first; db.names.back().first.append(", "); db.names.back().first.append(tmp); first_expr = false; @@ -1349,10 +1435,14 @@ parse_new_expr(const char* first, const char* last, C& db) return first; if (!first_expr) { + if (db.names.empty()) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); if (!tmp.empty()) { + if (db.names.empty()) + return first; db.names.back().first.append(", "); db.names.back().first.append(tmp); first_expr = false; @@ -1366,14 +1456,20 @@ parse_new_expr(const char* first, const char* last, C& db) typename C::String init_list; if (has_init) { + if (db.names.empty()) + return first; init_list = db.names.back().move_full(); db.names.pop_back(); } + if (db.names.empty()) + return first; auto type = db.names.back().move_full(); db.names.pop_back(); typename C::String expr_list; if (has_expr_list) { + if (db.names.empty()) + return first; expr_list = db.names.back().move_full(); db.names.pop_back(); } @@ -1435,10 +1531,14 @@ parse_conversion_expr(const char* first, const char* last, C& db) return first; if (!first_expr) { + if (db.names.empty()) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); if (!tmp.empty()) { + if (db.names.empty()) + return first; db.names.back().first.append(", "); db.names.back().first.append(tmp); first_expr = false; @@ -1449,6 +1549,8 @@ parse_conversion_expr(const char* first, const char* last, C& db) } ++t; } + if (db.names.size() < 2) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; @@ -1472,6 +1574,8 @@ parse_arrow_expr(const char* first, const char* last, C& db) const char* t1 = parse_expression(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += "->"; @@ -1564,6 +1668,8 @@ parse_function_type(const char* first, const char* last, C& db) sig += " &&"; break; } + if (db.names.empty()) + return first; db.names.back().first += " "; db.names.back().second.insert(0, sig); first = t; @@ -1587,6 +1693,8 @@ parse_pointer_to_member_type(const char* first, const char* last, C& db) const char* t2 = parse_type(t, last, db); if (t2 != t) { + if (db.names.size() < 2) + return first; auto func = std::move(db.names.back()); db.names.pop_back(); auto class_type = std::move(db.names.back()); @@ -1621,6 +1729,8 @@ parse_array_type(const char* first, const char* last, C& db) const char* t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; if (db.names.back().second.substr(0, 2) == " [") db.names.back().second.erase(0, 1); db.names.back().second.insert(0, " []"); @@ -1635,6 +1745,8 @@ parse_array_type(const char* first, const char* last, C& db) const char* t2 = parse_type(t+1, last, db); if (t2 != t+1) { + if (db.names.empty()) + return first; if (db.names.back().second.substr(0, 2) == " [") db.names.back().second.erase(0, 1); db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]"); @@ -1650,6 +1762,8 @@ parse_array_type(const char* first, const char* last, C& db) const char* t2 = parse_type(++t, last, db); if (t2 != t) { + if (db.names.size() < 2) + return first; auto type = std::move(db.names.back()); db.names.pop_back(); auto expr = std::move(db.names.back()); @@ -1682,6 +1796,8 @@ parse_decltype(const char* first, const char* last, C& db) const char* t = parse_expression(first+2, last, db); if (t != first+2 && t != last && *t == 'E') { + if (db.names.empty()) + return first; db.names.back() = "decltype(" + db.names.back().move_full() + ")"; first = t+1; } @@ -1719,6 +1835,8 @@ parse_vector_type(const char* first, const char* last, C& db) const char* t1 = parse_type(t, last, db); if (t1 != t) { + if (db.names.empty()) + return first; db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; first = t1; } @@ -1740,6 +1858,8 @@ parse_vector_type(const char* first, const char* last, C& db) const char* t = parse_expression(t1, last, db); if (t != t1) { + if (db.names.empty()) + return first; num = db.names.back().move_full(); db.names.pop_back(); t1 = t; @@ -1750,6 +1870,8 @@ parse_vector_type(const char* first, const char* last, C& db) const char* t = parse_type(t1, last, db); if (t != t1) { + if (db.names.empty()) + return first; db.names.back().first += " vector[" + num + "]"; first = t; } @@ -1860,6 +1982,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_array_type(first, last, db); if (t != first) { + if (db.names.empty()) + return first; first = t; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); } @@ -1868,6 +1992,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_type(first+1, last, db); if (t != first+1) { + if (db.names.empty()) + return first; db.names.back().first.append(" complex"); first = t; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); @@ -1877,6 +2003,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_function_type(first, last, db); if (t != first) { + if (db.names.empty()) + return first; first = t; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); } @@ -1885,6 +2013,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_type(first+1, last, db); if (t != first+1) { + if (db.names.empty()) + return first; db.names.back().first.append(" imaginary"); first = t; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); @@ -1894,6 +2024,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_pointer_to_member_type(first, last, db); if (t != first) { + if (db.names.empty()) + return first; first = t; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); } @@ -2021,6 +2153,8 @@ parse_type(const char* first, const char* last, C& db) const char* t2 = parse_type(t, last, db); if (t2 != t) { + if (db.names.size() < 2) + return first; auto type = db.names.back().move_full(); db.names.pop_back(); if (db.names.back().first.substr(0, 9) != "objcproto") @@ -2053,6 +2187,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_name(first, last, db); if (t != first) { + if (db.names.empty()) + return first; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); first = t; } @@ -2068,6 +2204,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_template_args(first, last, db); if (t != first) { + if (db.names.size() < 2) + return first; auto template_args = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += template_args; @@ -2103,6 +2241,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_decltype(first, last, db); if (t != first) { + if (db.names.empty()) + return first; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); first = t; return first; @@ -2112,6 +2252,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_vector_type(first, last, db); if (t != first) { + if (db.names.empty()) + return first; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); first = t; return first; @@ -2133,6 +2275,8 @@ parse_type(const char* first, const char* last, C& db) t = parse_name(first, last, db); if (t != first) { + if (db.names.empty()) + return first; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); first = t; } @@ -2169,6 +2313,7 @@ parse_type(const char* first, const char* last, C& db) // ::= gt # > // ::= ix # [] // ::= le # <= +// ::= li <source-name> # operator "" // ::= ls # << // ::= lS # <<= // ::= lt # < @@ -2251,6 +2396,8 @@ parse_operator_name(const char* first, const char* last, C& db) db.try_to_parse_template_args = try_to_parse_template_args; if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "operator "); db.parsed_ctor_dtor_cv = true; first = t; @@ -2328,6 +2475,18 @@ parse_operator_name(const char* first, const char* last, C& db) db.names.push_back("operator<="); first += 2; break; + case 'i': + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator\"\" "); + first = t; + } + } + break; case 's': db.names.push_back("operator<<"); first += 2; @@ -2472,6 +2631,8 @@ parse_operator_name(const char* first, const char* last, C& db) const char* t = parse_source_name(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "operator "); first = t; } @@ -2681,6 +2842,8 @@ parse_expr_primary(const char* first, const char* last, C& db) ; if (n != t && n != last && *n == 'E') { + if (db.names.empty()) + return first; db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); first = n+1; break; @@ -2781,6 +2944,8 @@ parse_ctor_dtor_name(const char* first, const char* last, C& db) case '2': case '3': case '5': + if (db.names.empty()) + return first; db.names.push_back(base_name(db.names.back().first)); first += 2; db.parsed_ctor_dtor_cv = true; @@ -2794,6 +2959,8 @@ parse_ctor_dtor_name(const char* first, const char* last, C& db) case '1': case '2': case '5': + if (db.names.empty()) + return first; db.names.push_back("~" + base_name(db.names.back().first)); first += 2; db.parsed_ctor_dtor_cv = true; @@ -2864,6 +3031,8 @@ parse_unnamed_type_name(const char* first, const char* last, C& db) db.names.pop_back(); return first; } + if (db.names.size() < 2) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); db.names.back().first.append(tmp); @@ -2873,6 +3042,8 @@ parse_unnamed_type_name(const char* first, const char* last, C& db) t1 = parse_type(t0, last, db); if (t1 == t0) break; + if (db.names.size() < 2) + return first; tmp = db.names.back().move_full(); db.names.pop_back(); if (!tmp.empty()) @@ -2987,7 +3158,11 @@ parse_unscoped_name(const char* first, const char* last, C& db) if (t1 != t0) { if (St) + { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "std::"); + } first = t1; } } @@ -3005,6 +3180,8 @@ parse_alignof_type(const char* first, const char* last, C& db) const char* t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; first = t; } @@ -3023,6 +3200,8 @@ parse_alignof_expr(const char* first, const char* last, C& db) const char* t = parse_expression(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; first = t; } @@ -3037,6 +3216,8 @@ parse_noexcept_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(first, last, db); if (t1 != first) { + if (db.names.empty()) + return first; db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; first = t1; } @@ -3050,6 +3231,8 @@ parse_prefix_expression(const char* first, const char* last, const typename C::S const char* t1 = parse_expression(first, last, db); if (t1 != first) { + if (db.names.empty()) + return first; db.names.back().first = op + "(" + db.names.back().move_full() + ")"; first = t1; } @@ -3066,6 +3249,8 @@ parse_binary_expression(const char* first, const char* last, const typename C::S const char* t2 = parse_expression(t1, last, db); if (t2 != t1) { + if (db.names.size() < 2) + return first; auto op2 = db.names.back().move_full(); db.names.pop_back(); auto op1 = db.names.back().move_full(); @@ -3216,6 +3401,8 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(t+2, last, db); if (t1 != t+2) { + if (db.names.empty()) + return first; db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + "delete[] " + db.names.back().move_full(); first = t1; @@ -3235,6 +3422,8 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(t+2, last, db); if (t1 != t+2) { + if (db.names.empty()) + return first; db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + "delete " + db.names.back().move_full(); first = t1; @@ -3305,6 +3494,8 @@ parse_expression(const char* first, const char* last, C& db) const char* t2 = parse_expression(t1, last, db); if (t2 != t1) { + if (db.names.size() < 2) + return first; auto op2 = db.names.back().move_full(); db.names.pop_back(); auto op1 = db.names.back().move_full(); @@ -3376,6 +3567,8 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(first+2, last, db); if (t1 != first+2) { + if (db.names.empty()) + return first; db.names.back() = "(" + db.names.back().move_full() + ")--"; first = t1; } @@ -3464,6 +3657,8 @@ parse_expression(const char* first, const char* last, C& db) const char* t1 = parse_expression(first+2, last, db); if (t1 != first+2) { + if (db.names.empty()) + return first; db.names.back() = "(" + db.names.back().move_full() + ")++"; first = t1; } @@ -3491,6 +3686,8 @@ parse_expression(const char* first, const char* last, C& db) const char* t3 = parse_expression(t2, last, db); if (t3 != t2) { + if (db.names.size() < 3) + return first; auto op3 = db.names.back().move_full(); db.names.pop_back(); auto op2 = db.names.back().move_full(); @@ -3918,6 +4115,8 @@ parse_local_name(const char* first, const char* last, C& db) { case 's': first = parse_discriminator(t+1, last); + if (db.names.empty()) + return first; db.names.back().first.append("::string literal"); break; case 'd': @@ -3930,6 +4129,8 @@ parse_local_name(const char* first, const char* last, C& db) t1 = parse_name(t, last, db); if (t1 != t) { + if (db.names.size() < 2) + return first; auto name = db.names.back().move_full(); db.names.pop_back(); db.names.back().first.append("::"); @@ -3948,6 +4149,8 @@ parse_local_name(const char* first, const char* last, C& db) { // parse but ignore discriminator first = parse_discriminator(t1, last); + if (db.names.size() < 2) + return first; auto name = db.names.back().move_full(); db.names.pop_back(); db.names.back().first.append("::"); @@ -4004,11 +4207,15 @@ parse_name(const char* first, const char* last, C& db) { if (t1 != last && *t1 == 'I') // <unscoped-template-name> <template-args> { + if (db.names.empty()) + return first; db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); t0 = t1; t1 = parse_template_args(t0, last, db); if (t1 != t0) { + if (db.names.size() < 2) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += tmp; @@ -4027,6 +4234,8 @@ parse_name(const char* first, const char* last, C& db) t1 = parse_template_args(t0, last, db); if (t1 != t0) { + if (db.names.size() < 2) + return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); db.names.back().first += tmp; @@ -4112,6 +4321,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "vtable for "); first = t; } @@ -4121,6 +4332,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "VTT for "); first = t; } @@ -4130,6 +4343,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "typeinfo for "); first = t; } @@ -4139,6 +4354,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_type(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "typeinfo name for "); first = t; } @@ -4155,6 +4372,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_encoding(t1, last, db); if (t != t1) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "covariant return thunk to "); first = t; } @@ -4171,6 +4390,8 @@ parse_special_name(const char* first, const char* last, C& db) const char* t1 = parse_type(++t0, last, db); if (t1 != t0) { + if (db.names.size() < 2) + return first; auto left = db.names.back().move_full(); db.names.pop_back(); db.names.back().first = "construction vtable for " + @@ -4190,6 +4411,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_encoding(t0, last, db); if (t != t0) { + if (db.names.empty()) + return first; if (first[2] == 'v') { db.names.back().first.insert(0, "virtual thunk to "); @@ -4213,6 +4436,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_name(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "guard variable for "); first = t; } @@ -4222,6 +4447,8 @@ parse_special_name(const char* first, const char* last, C& db) t = parse_name(first+2, last, db); if (t != first+2) { + if (db.names.empty()) + return first; db.names.back().first.insert(0, "reference temporary for "); first = t; } @@ -4233,6 +4460,26 @@ parse_special_name(const char* first, const char* last, C& db) return first; } +template <class T> +class save_value +{ + T& restore_; + T original_value_; +public: + save_value(T& restore) + : restore_(restore), + original_value_(restore) + {} + + ~save_value() + { + restore_ = std::move(original_value_); + } + + save_value(const save_value&) = delete; + save_value& operator=(const save_value&) = delete; +}; + // <encoding> ::= <function name> <bare-function-type> // ::= <data name> // ::= <special-name> @@ -4243,6 +4490,11 @@ parse_encoding(const char* first, const char* last, C& db) { if (first != last) { + save_value<decltype(db.encoding_depth)> su(db.encoding_depth); + ++db.encoding_depth; + save_value<decltype(db.tag_templates)> sb(db.tag_templates); + if (db.encoding_depth > 1) + db.tag_templates = true; switch (*first) { case 'G': @@ -4258,17 +4510,23 @@ parse_encoding(const char* first, const char* last, C& db) { if (t != last && *t != 'E' && *t != '.') { - bool tag_templates = db.tag_templates; + save_value<bool> sb2(db.tag_templates); db.tag_templates = false; const char* t2; typename C::String ret2; + if (db.names.empty()) + return first; const typename C::String& nm = db.names.back().first; + if (nm.empty()) + return first; if (!db.parsed_ctor_dtor_cv && nm.back() == '>' && nm[nm.size()-2] != '-' && nm[nm.size()-2] != '>') { t2 = parse_type(t, last, db); if (t2 == t) return first; + if (db.names.size() < 2) + return first; auto ret1 = std::move(db.names.back().first); ret2 = std::move(db.names.back().second); if (ret2.empty()) @@ -4305,6 +4563,8 @@ parse_encoding(const char* first, const char* last, C& db) db.names.pop_back(); if (!tmp.empty()) { + if (db.names.empty()) + return first; if (!first_arg) db.names.back().first += ", "; else @@ -4315,6 +4575,8 @@ parse_encoding(const char* first, const char* last, C& db) t = t2; } } + if (db.names.empty()) + return first; db.names.back().first += ')'; if (cv & 1) db.names.back().first.append(" const"); @@ -4328,7 +4590,6 @@ parse_encoding(const char* first, const char* last, C& db) db.names.back().first.append(" &&"); db.names.back().first += ret2; first = t; - db.tag_templates = tag_templates; } else first = t; @@ -4370,6 +4631,8 @@ parse_block_invoke(const char* first, const char* last, C& db) while (t != last && isdigit(*t)) ++t; } + if (db.names.empty()) + return first; db.names.back().first.insert(0, "invocation function for block in "); first = t; } @@ -4385,6 +4648,8 @@ parse_dot_suffix(const char* first, const char* last, C& db) { if (first != last && *first == '.') { + if (db.names.empty()) + return first; db.names.back().first += " (" + typename C::String(first, last) + ")"; first = last; } @@ -4620,6 +4885,7 @@ struct Db Vector<template_param_type> template_param; unsigned cv; unsigned ref; + unsigned encoding_depth; bool parsed_ctor_dtor_cv; bool tag_templates; bool fix_forward_references; @@ -4647,6 +4913,7 @@ __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) Db db(a); db.cv = 0; db.ref = 0; + db.encoding_depth = 0; db.parsed_ctor_dtor_cv = false; db.tag_templates = true; db.template_param.emplace_back(a); @@ -4699,8 +4966,7 @@ __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) return buf; } -} // unnamed namespace - +} #endif diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp index f90a097416df..d5758c09b1e2 100644 --- a/source/Core/Module.cpp +++ b/source/Core/Module.cpp @@ -33,6 +33,7 @@ #include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Symbol/SymbolFile.h" @@ -1499,30 +1500,19 @@ Module::SetArchitecture (const ArchSpec &new_arch) } bool -Module::SetLoadAddress (Target &target, lldb::addr_t offset, bool &changed) +Module::SetLoadAddress (Target &target, lldb::addr_t value, bool value_is_offset, bool &changed) { - size_t num_loaded_sections = 0; - SectionList *section_list = GetSectionList (); - if (section_list) + ObjectFile *object_file = GetObjectFile(); + if (object_file) { - const size_t num_sections = section_list->GetSize(); - size_t sect_idx = 0; - for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) - { - // Iterate through the object file sections to find the - // first section that starts of file offset zero and that - // has bytes in the file... - SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); - // Only load non-thread specific sections when given a slide - if (section_sp && !section_sp->IsThreadSpecific()) - { - if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + offset)) - ++num_loaded_sections; - } - } + changed = object_file->SetLoadAddress(target, value, value_is_offset); + return true; } - changed = num_loaded_sections > 0; - return num_loaded_sections > 0; + else + { + changed = false; + } + return false; } diff --git a/source/Core/Opcode.cpp b/source/Core/Opcode.cpp index 978a110150f9..2bf7f5eae9b0 100644 --- a/source/Core/Opcode.cpp +++ b/source/Core/Opcode.cpp @@ -71,6 +71,10 @@ Opcode::Dump (Stream *s, uint32_t min_byte_width) lldb::ByteOrder Opcode::GetDataByteOrder () const { + if (m_byte_order != eByteOrderInvalid) + { + return m_byte_order; + } switch (m_type) { case Opcode::eTypeInvalid: break; @@ -89,39 +93,66 @@ uint32_t Opcode::GetData (DataExtractor &data) const { uint32_t byte_size = GetByteSize (); + uint8_t swap_buf[8]; + const void *buf = NULL; - DataBufferSP buffer_sp; if (byte_size > 0) { - switch (m_type) + if (!GetEndianSwap()) + { + if (m_type == Opcode::eType16_2) + { + // 32 bit thumb instruction, we need to sizzle this a bit + swap_buf[0] = m_data.inst.bytes[2]; + swap_buf[1] = m_data.inst.bytes[3]; + swap_buf[2] = m_data.inst.bytes[0]; + swap_buf[3] = m_data.inst.bytes[1]; + buf = swap_buf; + } + else + { + buf = GetOpcodeDataBytes(); + } + } + else { - case Opcode::eTypeInvalid: - break; - - case Opcode::eType8: buffer_sp.reset (new DataBufferHeap (&m_data.inst8, byte_size)); break; - case Opcode::eType16: buffer_sp.reset (new DataBufferHeap (&m_data.inst16, byte_size)); break; - case Opcode::eType16_2: - { - // 32 bit thumb instruction, we need to sizzle this a bit - uint8_t buf[4]; - buf[0] = m_data.inst.bytes[2]; - buf[1] = m_data.inst.bytes[3]; - buf[2] = m_data.inst.bytes[0]; - buf[3] = m_data.inst.bytes[1]; - buffer_sp.reset (new DataBufferHeap (buf, byte_size)); - } - break; - case Opcode::eType32: - buffer_sp.reset (new DataBufferHeap (&m_data.inst32, byte_size)); - break; - case Opcode::eType64: buffer_sp.reset (new DataBufferHeap (&m_data.inst64, byte_size)); break; - case Opcode::eTypeBytes:buffer_sp.reset (new DataBufferHeap (GetOpcodeBytes(), byte_size)); break; - break; + switch (m_type) + { + case Opcode::eTypeInvalid: + break; + case Opcode::eType8: + buf = GetOpcodeDataBytes(); + break; + case Opcode::eType16: + *(uint16_t *)swap_buf = llvm::ByteSwap_16(m_data.inst16); + buf = swap_buf; + break; + case Opcode::eType16_2: + swap_buf[0] = m_data.inst.bytes[1]; + swap_buf[1] = m_data.inst.bytes[0]; + swap_buf[2] = m_data.inst.bytes[3]; + swap_buf[3] = m_data.inst.bytes[2]; + buf = swap_buf; + break; + case Opcode::eType32: + *(uint32_t *)swap_buf = llvm::ByteSwap_32(m_data.inst32); + buf = swap_buf; + break; + case Opcode::eType64: + *(uint32_t *)swap_buf = llvm::ByteSwap_64(m_data.inst64); + buf = swap_buf; + break; + case Opcode::eTypeBytes: + buf = GetOpcodeDataBytes(); + break; + } } } - - if (buffer_sp) + if (buf) { + DataBufferSP buffer_sp; + + buffer_sp.reset (new DataBufferHeap (buf, byte_size)); data.SetByteOrder(GetDataByteOrder()); data.SetData (buffer_sp); return byte_size; diff --git a/source/Core/Section.cpp b/source/Core/Section.cpp index e2a084ceb2f2..28d7d93b9bb9 100644 --- a/source/Core/Section.cpp +++ b/source/Core/Section.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Section.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" using namespace lldb; diff --git a/source/Core/SourceManager.cpp b/source/Core/SourceManager.cpp index 940034625c0a..0a6a80401d8d 100644 --- a/source/Core/SourceManager.cpp +++ b/source/Core/SourceManager.cpp @@ -432,6 +432,56 @@ SourceManager::File::GetLineOffset (uint32_t line) return UINT32_MAX; } +uint32_t +SourceManager::File::GetNumLines () +{ + CalculateLineOffsets(); + return m_offsets.size(); +} + +const char * +SourceManager::File::PeekLineData (uint32_t line) +{ + if (!LineIsValid(line)) + return NULL; + + size_t line_offset = GetLineOffset (line); + if (line_offset < m_data_sp->GetByteSize()) + return (const char *)m_data_sp->GetBytes() + line_offset; + return NULL; +} + +uint32_t +SourceManager::File::GetLineLength (uint32_t line, bool include_newline_chars) +{ + if (!LineIsValid(line)) + return false; + + size_t start_offset = GetLineOffset (line); + size_t end_offset = GetLineOffset (line + 1); + if (end_offset == UINT32_MAX) + end_offset = m_data_sp->GetByteSize(); + + if (end_offset > start_offset) + { + uint32_t length = end_offset - start_offset; + if (include_newline_chars == false) + { + const char *line_start = (const char *)m_data_sp->GetBytes() + start_offset; + while (length > 0) + { + const char last_char = line_start[length-1]; + if ((last_char == '\r') || (last_char == '\n')) + --length; + else + break; + } + } + return length; + } + return 0; +} + bool SourceManager::File::LineIsValid (uint32_t line) { diff --git a/source/Core/StreamAsynchronousIO.cpp b/source/Core/StreamAsynchronousIO.cpp index b9e5cdfde721..257982ab8b29 100644 --- a/source/Core/StreamAsynchronousIO.cpp +++ b/source/Core/StreamAsynchronousIO.cpp @@ -28,25 +28,26 @@ StreamAsynchronousIO::StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t b StreamAsynchronousIO::~StreamAsynchronousIO () { + // Flush when we destroy to make sure we display the data + Flush(); } void StreamAsynchronousIO::Flush () { - if (m_accumulated_data.GetSize() > 0) + if (!m_accumulated_data.empty()) { std::unique_ptr<EventDataBytes> data_bytes_ap (new EventDataBytes); // Let's swap the bytes to avoid LARGE string copies. - data_bytes_ap->SwapBytes (m_accumulated_data.GetString()); + data_bytes_ap->SwapBytes (m_accumulated_data); EventSP new_event_sp (new Event (m_broadcast_event_type, data_bytes_ap.release())); m_broadcaster.BroadcastEvent (new_event_sp); - m_accumulated_data.Clear(); } } size_t StreamAsynchronousIO::Write (const void *s, size_t length) { - m_accumulated_data.Write (s, length); + m_accumulated_data.append ((const char *)s, length); return length; } diff --git a/source/Core/StringList.cpp b/source/Core/StringList.cpp index 994975116789..d2fa8cfb4a88 100644 --- a/source/Core/StringList.cpp +++ b/source/Core/StringList.cpp @@ -56,6 +56,12 @@ StringList::AppendString (const std::string &s) } void +StringList::AppendString (std::string &&s) +{ + m_strings.push_back (s); +} + +void StringList::AppendString (const char *str, size_t str_len) { if (str) @@ -93,6 +99,20 @@ StringList::GetSize () const return m_strings.size(); } +size_t +StringList::GetMaxStringLength () const +{ + size_t max_length = 0; + for (const auto &s : m_strings) + { + const size_t len = s.size(); + if (max_length < len) + max_length = len; + } + return max_length; +} + + const char * StringList::GetStringAtIndex (size_t idx) const { @@ -126,37 +146,39 @@ StringList::Clear () void StringList::LongestCommonPrefix (std::string &common_prefix) { - //arg_sstr_collection::iterator pos, end = m_args.end(); - size_t pos = 0; - size_t end = m_strings.size(); + const size_t num_strings = m_strings.size(); - if (pos == end) + if (num_strings == 0) + { common_prefix.clear(); + } else - common_prefix = m_strings[pos]; - - for (++pos; pos != end; ++pos) { - size_t new_size = strlen (m_strings[pos].c_str()); + common_prefix = m_strings.front(); - // First trim common_prefix if it is longer than the current element: - if (common_prefix.size() > new_size) - common_prefix.erase (new_size); + for (size_t idx = 1; idx < num_strings; ++idx) + { + std::string &curr_string = m_strings[idx]; + size_t new_size = curr_string.size(); - // Then trim it at the first disparity: + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); - for (size_t i = 0; i < common_prefix.size(); i++) - { - if (m_strings[pos][i] != common_prefix[i]) + // Then trim it at the first disparity: + for (size_t i = 0; i < common_prefix.size(); i++) { - common_prefix.erase(i); - break; + if (curr_string[i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } } - } - // If we've emptied the common prefix, we're done. - if (common_prefix.empty()) - break; + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } } } @@ -173,6 +195,24 @@ StringList::InsertStringAtIndex (size_t idx, const char *str) } void +StringList::InsertStringAtIndex (size_t idx, const std::string &str) +{ + if (idx < m_strings.size()) + m_strings.insert (m_strings.begin() + idx, str); + else + m_strings.push_back (str); +} + +void +StringList::InsertStringAtIndex (size_t idx, std::string &&str) +{ + if (idx < m_strings.size()) + m_strings.insert (m_strings.begin() + idx, str); + else + m_strings.push_back (str); +} + +void StringList::DeleteStringAtIndex (size_t idx) { if (idx < m_strings.size()) @@ -180,6 +220,12 @@ StringList::DeleteStringAtIndex (size_t idx) } size_t +StringList::SplitIntoLines (const std::string &lines) +{ + return SplitIntoLines (lines.c_str(), lines.size()); +} + +size_t StringList::SplitIntoLines (const char *lines, size_t len) { const size_t orig_size = m_strings.size(); @@ -231,8 +277,7 @@ StringList::RemoveBlankLines () } std::string -StringList::CopyList(const char* item_preamble, - const char* items_sep) +StringList::CopyList(const char* item_preamble, const char* items_sep) const { StreamString strm; for (size_t i = 0; i < GetSize(); i++) diff --git a/source/Core/Value.cpp b/source/Core/Value.cpp index 9dd72c7546bf..9d42a3774624 100644 --- a/source/Core/Value.cpp +++ b/source/Core/Value.cpp @@ -26,6 +26,7 @@ #include "lldb/Symbol/Variable.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" using namespace lldb; diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp index d39d21a2a0bf..10e5ab452f0f 100644 --- a/source/Core/ValueObject.cpp +++ b/source/Core/ValueObject.cpp @@ -50,6 +50,7 @@ #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" @@ -977,14 +978,14 @@ ValueObject::GetPointeeData (DataExtractor& data, ValueObjectSP pointee_sp = Dereference(error); if (error.Fail() || pointee_sp.get() == NULL) return 0; - return pointee_sp->GetDataExtractor().Copy(data); + return pointee_sp->GetData(data); } else { ValueObjectSP child_sp = GetChildAtIndex(0, true); if (child_sp.get() == NULL) return 0; - return child_sp->GetDataExtractor().Copy(data); + return child_sp->GetData(data); } return true; } @@ -1388,99 +1389,33 @@ ValueObject::GetObjectDescription () } bool -ValueObject::GetValueAsCString (lldb::Format format, +ValueObject::GetValueAsCString (const lldb_private::TypeFormatImpl& format, std::string& destination) { - if (GetClangType().IsAggregateType () == false && UpdateValueIfNeeded(false)) - { - const Value::ContextType context_type = m_value.GetContextType(); - - if (context_type == Value::eContextTypeRegisterInfo) - { - const RegisterInfo *reg_info = m_value.GetRegisterInfo(); - if (reg_info) - { - ExecutionContext exe_ctx (GetExecutionContextRef()); - - StreamString reg_sstr; - m_data.Dump (®_sstr, - 0, - format, - reg_info->byte_size, - 1, - UINT32_MAX, - LLDB_INVALID_ADDRESS, - 0, - 0, - exe_ctx.GetBestExecutionContextScope()); - destination.swap(reg_sstr.GetString()); - } - } - else - { - ClangASTType clang_type = GetClangType (); - if (clang_type) - { - // put custom bytes to display in this DataExtractor to override the default value logic - lldb_private::DataExtractor special_format_data; - if (format == eFormatCString) - { - Flags type_flags(clang_type.GetTypeInfo(NULL)); - if (type_flags.Test(ClangASTType::eTypeIsPointer) && !type_flags.Test(ClangASTType::eTypeIsObjC)) - { - // if we are dumping a pointer as a c-string, get the pointee data as a string - TargetSP target_sp(GetTargetSP()); - if (target_sp) - { - size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); - Error error; - DataBufferSP buffer_sp(new DataBufferHeap(max_len+1,0)); - Address address(GetPointerValue()); - if (target_sp->ReadCStringFromMemory(address, (char*)buffer_sp->GetBytes(), max_len, error) && error.Success()) - special_format_data.SetData(buffer_sp); - } - } - } - - StreamString sstr; - ExecutionContext exe_ctx (GetExecutionContextRef()); - clang_type.DumpTypeValue (&sstr, // The stream to use for display - format, // Format to display this type with - special_format_data.GetByteSize() ? - special_format_data: m_data, // Data to extract from - 0, // Byte offset into "m_data" - GetByteSize(), // Byte size of item in "m_data" - GetBitfieldBitSize(), // Bitfield bit size - GetBitfieldBitOffset(), // Bitfield bit offset - exe_ctx.GetBestExecutionContextScope()); - // Don't set the m_error to anything here otherwise - // we won't be able to re-format as anything else. The - // code for ClangASTType::DumpTypeValue() should always - // return something, even if that something contains - // an error messsage. "m_error" is used to detect errors - // when reading the valid object, not for formatting errors. - if (sstr.GetString().empty()) - destination.clear(); - else - destination.swap(sstr.GetString()); - } - } - return !destination.empty(); - } + if (UpdateValueIfNeeded(false)) + return format.FormatObject(this,destination); else return false; } +bool +ValueObject::GetValueAsCString (lldb::Format format, + std::string& destination) +{ + return GetValueAsCString(TypeFormatImpl_Format(format),destination); +} + const char * ValueObject::GetValueAsCString () { if (UpdateValueIfNeeded(true)) { + lldb::TypeFormatImplSP format_sp; lldb::Format my_format = GetFormat(); if (my_format == lldb::eFormatDefault) { if (m_type_format_sp) - my_format = m_type_format_sp->GetFormat(); + format_sp = m_type_format_sp; else { if (m_is_bitfield_for_scalar) @@ -1503,7 +1438,9 @@ ValueObject::GetValueAsCString () if (my_format != m_last_format || m_value_str.empty()) { m_last_format = my_format; - if (GetValueAsCString(my_format, m_value_str)) + if (!format_sp) + format_sp.reset(new TypeFormatImpl_Format(my_format)); + if (GetValueAsCString(*format_sp.get(), m_value_str)) { if (!m_value_did_change && m_old_value_valid) { @@ -1609,7 +1546,8 @@ bool ValueObject::DumpPrintableRepresentation(Stream& s, ValueObjectRepresentationStyle val_obj_display, Format custom_format, - PrintableRepresentationSpecialCases special) + PrintableRepresentationSpecialCases special, + bool do_dump_error) { Flags flags(GetTypeInfo()); @@ -1808,7 +1746,12 @@ ValueObject::DumpPrintableRepresentation(Stream& s, else { if (m_error.Fail()) - s.Printf("<%s>", m_error.AsCString()); + { + if (do_dump_error) + s.Printf("<%s>", m_error.AsCString()); + else + return false; + } else if (val_obj_display == eValueObjectRepresentationStyleSummary) s.PutCString("<no summary available>"); else if (val_obj_display == eValueObjectRepresentationStyleValue) @@ -3515,7 +3458,8 @@ ValueObject::CreateConstantValue (const ConstString &name) if (!valobj_sp) { - valobj_sp = ValueObjectConstResult::Create (NULL, m_error); + ExecutionContext exe_ctx (GetExecutionContextRef()); + valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), m_error); } return valobj_sp; } @@ -3769,7 +3713,8 @@ ValueObject::EvaluationPoint::SyncWithProcessState() { // Start with the target, if it is NULL, then we're obviously not going to get any further: - ExecutionContext exe_ctx(m_exe_ctx_ref.Lock()); + const bool thread_and_frame_only_if_stopped = true; + ExecutionContext exe_ctx(m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped)); if (exe_ctx.GetTargetPtr() == NULL) return false; diff --git a/source/Core/ValueObjectChild.cpp b/source/Core/ValueObjectChild.cpp index 23add1ccf0e8..ccf87cd15b24 100644 --- a/source/Core/ValueObjectChild.cpp +++ b/source/Core/ValueObjectChild.cpp @@ -206,8 +206,12 @@ ValueObjectChild::UpdateValue () if (m_error.Success()) { - ExecutionContext exe_ctx (GetExecutionContextRef().Lock()); - m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + const bool thread_and_frame_only_if_stopped = true; + ExecutionContext exe_ctx (GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped)); + if (GetClangType().GetTypeInfo() & ClangASTType::eTypeHasValue) + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + else + m_error.Clear(); // No value so nothing to read... } } else diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp index 3d8b07a36948..2e5bb22a890c 100644 --- a/source/Core/ValueObjectVariable.cpp +++ b/source/Core/ValueObjectVariable.cpp @@ -328,6 +328,12 @@ ValueObjectVariable::GetLocationAsCString () bool ValueObjectVariable::SetValueFromCString (const char *value_str, Error& error) { + if (!UpdateValueIfNeeded()) + { + error.SetErrorString("unable to update value before writing"); + return false; + } + if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) { RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo(); @@ -360,6 +366,12 @@ ValueObjectVariable::SetValueFromCString (const char *value_str, Error& error) bool ValueObjectVariable::SetData (DataExtractor &data, Error &error) { + if (!UpdateValueIfNeeded()) + { + error.SetErrorString("unable to update value before writing"); + return false; + } + if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) { RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo(); @@ -371,7 +383,7 @@ ValueObjectVariable::SetData (DataExtractor &data, Error &error) error.SetErrorString("unable to retrieve register info"); return false; } - error = reg_value.SetValueFromData(reg_info, data, 0, false); + error = reg_value.SetValueFromData(reg_info, data, 0, true); if (error.Fail()) return false; if (reg_ctx->WriteRegister (reg_info, reg_value)) diff --git a/source/DataFormatters/DataVisualization.cpp b/source/DataFormatters/DataVisualization.cpp index 48d3517750aa..c2c2206f3449 100644 --- a/source/DataFormatters/DataVisualization.cpp +++ b/source/DataFormatters/DataVisualization.cpp @@ -204,35 +204,35 @@ DataVisualization::Categories::GetCategoryAtIndex (size_t index) bool DataVisualization::NamedSummaryFormats::GetSummaryFormat (const ConstString &type, lldb::TypeSummaryImplSP &entry) { - return GetFormatManager().GetNamedSummaryNavigator().Get(type,entry); + return GetFormatManager().GetNamedSummaryContainer().Get(type,entry); } void DataVisualization::NamedSummaryFormats::Add (const ConstString &type, const lldb::TypeSummaryImplSP &entry) { - GetFormatManager().GetNamedSummaryNavigator().Add(FormatManager::GetValidTypeName(type),entry); + GetFormatManager().GetNamedSummaryContainer().Add(FormatManager::GetValidTypeName(type),entry); } bool DataVisualization::NamedSummaryFormats::Delete (const ConstString &type) { - return GetFormatManager().GetNamedSummaryNavigator().Delete(type); + return GetFormatManager().GetNamedSummaryContainer().Delete(type); } void DataVisualization::NamedSummaryFormats::Clear () { - GetFormatManager().GetNamedSummaryNavigator().Clear(); + GetFormatManager().GetNamedSummaryContainer().Clear(); } void DataVisualization::NamedSummaryFormats::LoopThrough (TypeSummaryImpl::SummaryCallback callback, void* callback_baton) { - GetFormatManager().GetNamedSummaryNavigator().LoopThrough(callback, callback_baton); + GetFormatManager().GetNamedSummaryContainer().LoopThrough(callback, callback_baton); } uint32_t DataVisualization::NamedSummaryFormats::GetCount () { - return GetFormatManager().GetNamedSummaryNavigator().GetCount(); + return GetFormatManager().GetNamedSummaryContainer().GetCount(); } diff --git a/source/DataFormatters/FormatManager.cpp b/source/DataFormatters/FormatManager.cpp index bec2edf5d5c2..751e61284610 100644 --- a/source/DataFormatters/FormatManager.cpp +++ b/source/DataFormatters/FormatManager.cpp @@ -741,12 +741,12 @@ AddFormat (TypeCategoryImpl::SharedPointer category_sp, TypeFormatImpl::Flags flags, bool regex = false) { - lldb::TypeFormatImplSP format_sp(new TypeFormatImpl(format, flags)); + lldb::TypeFormatImplSP format_sp(new TypeFormatImpl_Format(format, flags)); if (regex) - category_sp->GetRegexValueNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),format_sp); + category_sp->GetRegexTypeFormatsContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),format_sp); else - category_sp->GetValueNavigator()->Add(type_name, format_sp); + category_sp->GetTypeFormatsContainer()->Add(type_name, format_sp); } @@ -761,9 +761,9 @@ AddStringSummary(TypeCategoryImpl::SharedPointer category_sp, string)); if (regex) - category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); else - category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); + category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp); } #ifndef LLDB_DISABLE_PYTHON @@ -782,9 +782,9 @@ AddScriptSummary(TypeCategoryImpl::SharedPointer category_sp, funct_name, code.c_str())); if (regex) - category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); else - category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); + category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp); } #endif @@ -799,9 +799,9 @@ AddCXXSummary (TypeCategoryImpl::SharedPointer category_sp, { lldb::TypeSummaryImplSP summary_sp(new CXXFunctionSummaryFormat(flags,funct,description)); if (regex) - category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); else - category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); + category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp); } #endif @@ -815,9 +815,27 @@ static void AddCXXSynthetic (TypeCategoryImpl::SharedPointer category_sp, { lldb::SyntheticChildrenSP synth_sp(new CXXSyntheticChildren(flags,description,generator)); if (regex) - category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())), synth_sp); + category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())), synth_sp); else - category_sp->GetSyntheticNavigator()->Add(type_name,synth_sp); + category_sp->GetTypeSyntheticsContainer()->Add(type_name,synth_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +static void AddFilter (TypeCategoryImpl::SharedPointer category_sp, + std::vector<std::string> children, + const char* description, + ConstString type_name, + ScriptedSyntheticChildren::Flags flags, + bool regex = false) +{ + TypeFilterImplSP filter_sp(new TypeFilterImpl(flags)); + for (auto child : children) + filter_sp->AddExpressionPath(child); + if (regex) + category_sp->GetRegexTypeFiltersContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())), filter_sp); + else + category_sp->GetTypeFiltersContainer()->Add(type_name,filter_sp); } #endif @@ -838,26 +856,26 @@ FormatManager::LoadLibStdcppFormatters() TypeCategoryImpl::SharedPointer gnu_category_sp = GetCategory(m_gnu_cpp_category_name); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::string"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::string"), std_string_summary_sp); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string<char>"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string<char>"), std_string_summary_sp); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string<char,std::char_traits<char>,std::allocator<char> >"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string<char,std::char_traits<char>,std::allocator<char> >"), std_string_summary_sp); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string<char, std::char_traits<char>, std::allocator<char> >"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string<char, std::char_traits<char>, std::allocator<char> >"), std_string_summary_sp); // making sure we force-pick the summary for printing wstring (_M_p is a wchar_t*) lldb::TypeSummaryImplSP std_wstring_summary_sp(new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}")); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::wstring"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::wstring"), std_wstring_summary_sp); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string<wchar_t>"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string<wchar_t>"), std_wstring_summary_sp); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >"), std_wstring_summary_sp); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >"), std_wstring_summary_sp); @@ -866,24 +884,24 @@ FormatManager::LoadLibStdcppFormatters() SyntheticChildren::Flags stl_synth_flags; stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); - gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), + gnu_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); - gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), + gnu_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider"))); - gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + gnu_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(true); - gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), + gnu_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); - gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), + gnu_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); - gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + gnu_category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); @@ -891,10 +909,10 @@ FormatManager::LoadLibStdcppFormatters() AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true); - gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::vector<std::allocator<bool> >"), + gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::vector<std::allocator<bool> >"), TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); - gnu_category_sp->GetSyntheticNavigator()->Add(ConstString("std::vector<std::allocator<bool> >"), + gnu_category_sp->GetTypeSyntheticsContainer()->Add(ConstString("std::vector<std::allocator<bool> >"), SyntheticChildrenSP(new CXXSyntheticChildren(stl_synth_flags,"libc++ std::vector<bool> synthetic children",lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator))); #endif @@ -921,14 +939,14 @@ FormatManager::LoadLibcxxFormatters() TypeCategoryImpl::SharedPointer libcxx_category_sp = GetCategory(m_libcxx_category_name); - libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::string"), + libcxx_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__1::string"), std_string_summary_sp); - libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >"), + libcxx_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >"), std_string_summary_sp); - libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::wstring"), + libcxx_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__1::wstring"), std_wstring_summary_sp); - libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >"), + libcxx_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >"), std_wstring_summary_sp); SyntheticChildren::Flags stl_synth_flags; @@ -943,7 +961,7 @@ FormatManager::LoadLibcxxFormatters() AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multimap synthetic children", ConstString("^std::__1::multimap<.+> >(( )?&)?$"), stl_synth_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator, "libc++ std::unordered containers synthetic children", ConstString("^(std::__1::)unordered_(multi)?(map|set)<.+> >$"), stl_synth_flags, true); - libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")), + libcxx_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, "lldb.formatters.cpp.libcxx.stddeque_SynthProvider"))); @@ -963,13 +981,15 @@ FormatManager::LoadLibcxxFormatters() AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::unordered containers summary provider", ConstString("^(std::__1::)unordered_(multi)?(map|set)<.+> >$"), stl_summary_flags, true); stl_summary_flags.SetSkipPointers(true); - AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, true); - AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::weak_ptr<.+>(( )?&)?$"), stl_summary_flags, true); + + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxSmartPointerSummaryProvider, "libc++ std::shared_ptr summary provider", ConstString("^std::__1::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxSmartPointerSummaryProvider, "libc++ std::weak_ptr summary provider", ConstString("^std::__1::weak_ptr<.+>(( )?&)?$"), stl_summary_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^std::__1::__wrap_iter<.+>$"), stl_synth_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::__1::__map_iterator<.+>$"), stl_synth_flags, true); + AddFilter(libcxx_category_sp, {"__a_"}, "libc++ std::atomic filter", ConstString("^std::__1::atomic<.*>$"), stl_synth_flags, true); #endif } @@ -1002,9 +1022,9 @@ FormatManager::LoadSystemFormatters() TypeCategoryImpl::SharedPointer sys_category_sp = GetCategory(m_system_category_name); - sys_category_sp->GetSummaryNavigator()->Add(ConstString("char *"), string_format); - sys_category_sp->GetSummaryNavigator()->Add(ConstString("unsigned char *"), string_format); - sys_category_sp->GetRegexSummaryNavigator()->Add(any_size_char_arr, string_array_format); + sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("char *"), string_format); + sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("unsigned char *"), string_format); + sys_category_sp->GetRegexTypeSummariesContainer()->Add(any_size_char_arr, string_array_format); lldb::TypeSummaryImplSP ostype_summary(new StringSummaryFormat(TypeSummaryImpl::Flags().SetCascades(false) .SetSkipPointers(true) @@ -1015,10 +1035,10 @@ FormatManager::LoadSystemFormatters() .SetHideItemNames(false), "${var%O}")); - sys_category_sp->GetSummaryNavigator()->Add(ConstString("OSType"), ostype_summary); + sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("OSType"), ostype_summary); #ifndef LLDB_DISABLE_PYTHON - // FIXME because of a bug in the FormatNavigator we need to add a summary for both X* and const X* (<rdar://problem/12717717>) + // FIXME because of a bug in the FormattersContainer we need to add a summary for both X* and const X* (<rdar://problem/12717717>) AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16StringSummaryProvider, "char16_t * summary provider", ConstString("char16_t *"), string_flags); AddCXXSummary(sys_category_sp, lldb_private::formatters::Char32StringSummaryProvider, "char32_t * summary provider", ConstString("char32_t *"), string_flags); @@ -1065,11 +1085,11 @@ FormatManager::LoadObjCFormatters() TypeCategoryImpl::SharedPointer objc_category_sp = GetCategory(m_objc_category_name); lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat(objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider,"")); - objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL"), + objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL"), ObjC_BOOL_summary); - objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL &"), + objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL &"), ObjC_BOOL_summary); - objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL *"), + objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL *"), ObjC_BOOL_summary); #ifndef LLDB_DISABLE_PYTHON @@ -1256,6 +1276,7 @@ FormatManager::LoadObjCFormatters() AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSString"), appkit_flags); AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFStringRef"), appkit_flags); + AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__CFString"), appkit_flags); AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFMutableStringRef"), appkit_flags); AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSMutableString"), appkit_flags); AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFConstantString"), appkit_flags); @@ -1286,7 +1307,7 @@ FormatManager::LoadObjCFormatters() AddStringSummary(appkit_category_sp,"name:${var.name%S} reason:${var.reason%S}",ConstString("NSException"),appkit_flags); AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSNumber"), appkit_flags); - AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "CFNumberRef summary provider", ConstString("CFNumberRef"), appkit_flags); + AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "CFNumberRef summary provider", ConstString("CFNumberRef"), appkit_flags); AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFBoolean"), appkit_flags); AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFNumber"), appkit_flags); AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFBoolean"), appkit_flags); @@ -1306,7 +1327,7 @@ FormatManager::LoadObjCFormatters() AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSCalendarDate"), appkit_flags); AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("NSTimeZone"), appkit_flags); - AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("CFTimeZoneRef"), appkit_flags); + AddCXXSummary(corefoundation_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("CFTimeZoneRef"), appkit_flags); AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("__NSTimeZone"), appkit_flags); // CFAbsoluteTime is actually a double rather than a pointer to an object @@ -1318,7 +1339,7 @@ F |