diff options
Diffstat (limited to 'source/Target')
28 files changed, 1947 insertions, 511 deletions
diff --git a/source/Target/CPPLanguageRuntime.cpp b/source/Target/CPPLanguageRuntime.cpp index f5b7f7fc41a63..f048c6706a9b8 100644 --- a/source/Target/CPPLanguageRuntime.cpp +++ b/source/Target/CPPLanguageRuntime.cpp @@ -11,6 +11,8 @@ #include <string.h> +#include "llvm/ADT/StringRef.h" + #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Target/ExecutionContext.h" @@ -190,30 +192,17 @@ CPPLanguageRuntime::IsCPPMangledName (const char *name) } bool -CPPLanguageRuntime::StripNamespacesFromVariableName (const char *name, const char *&base_name_start, const char *&base_name_end) +CPPLanguageRuntime::ExtractContextAndIdentifier (const char *name, llvm::StringRef &context, llvm::StringRef &identifier) { - if (base_name_end == NULL) - base_name_end = name + strlen (name); - - const char *last_colon = strrchr (name, ':'); - - if (last_colon == NULL) - { - base_name_start = name; - return true; - } - - // Can't have a C++ name that begins with a single ':', nor contains an internal single ':' - if (last_colon == name) - return false; - else if (last_colon[-1] != ':') - return false; - else + static RegularExpression g_basename_regex("^(([A-Za-z_][A-Za-z_0-9]*::)*)([A-Za-z_][A-Za-z_0-9]*)$"); + RegularExpression::Match match(4); + if (g_basename_regex.Execute (name, &match)) { - // FIXME: should check if there is - base_name_start = last_colon + 1; + match.GetMatchAtIndex(name, 1, context); + match.GetMatchAtIndex(name, 3, identifier); return true; } + return false; } uint32_t @@ -282,6 +271,7 @@ ReverseFindMatchingChars (const llvm::StringRef &s, return false; } + void CPPLanguageRuntime::MethodName::Parse() { @@ -306,6 +296,7 @@ CPPLanguageRuntime::MethodName::Parse() if (arg_start > 0) { size_t basename_end = arg_start; + size_t context_start = 0; size_t context_end = llvm::StringRef::npos; if (basename_end > 0 && full[basename_end-1] == '>') { @@ -314,16 +305,35 @@ CPPLanguageRuntime::MethodName::Parse() size_t template_start, template_end; llvm::StringRef lt_gt("<>", 2); if (ReverseFindMatchingChars (full, lt_gt, template_start, template_end, basename_end)) + { context_end = full.rfind(':', template_start); + if (context_end == llvm::StringRef::npos) + { + // Check for templated functions that include return type like: + // 'void foo<Int>()' + context_end = full.rfind(' ', template_start); + if (context_end != llvm::StringRef::npos) + { + context_start = context_end; + } + } + } + else + { + context_end = full.rfind(':', basename_end); + } } - if (context_end == llvm::StringRef::npos) + else if (context_end == llvm::StringRef::npos) + { context_end = full.rfind(':', basename_end); + } if (context_end == llvm::StringRef::npos) m_basename = full.substr(0, basename_end); else { - m_context = full.substr(0, context_end - 1); + if (context_start < context_end) + m_context = full.substr(context_start, context_end - 1); const size_t basename_begin = context_end + 1; m_basename = full.substr(basename_begin, basename_end - basename_begin); } @@ -343,6 +353,30 @@ CPPLanguageRuntime::MethodName::Parse() // printf (" arguments = '%s'\n", m_arguments.str().c_str()); // if (!m_qualifiers.empty()) // printf ("qualifiers = '%s'\n", m_qualifiers.str().c_str()); + + // Make sure we have a valid C++ basename with optional template args + static RegularExpression g_identifier_regex("^~?([A-Za-z_][A-Za-z_0-9]*)(<.*>)?$"); + std::string basename_str(m_basename.str()); + bool basename_is_valid = g_identifier_regex.Execute (basename_str.c_str(), NULL); + if (!basename_is_valid) + { + // Check for C++ operators + if (m_basename.startswith("operator")) + { + static RegularExpression g_operator_regex("^(operator)( ?)([A-Za-z_][A-Za-z_0-9]*|\\(\\)|\\[\\]|[\\^<>=!\\/*+-]+)(<.*>)?(\\[\\])?$"); + basename_is_valid = g_operator_regex.Execute(basename_str.c_str(), NULL); + } + } + if (!basename_is_valid) + { + // The C++ basename doesn't match our regular expressions so this can't + // be a valid C++ method, clear everything out and indicate an error + m_context = llvm::StringRef(); + m_basename = llvm::StringRef(); + m_arguments = llvm::StringRef(); + m_qualifiers = llvm::StringRef(); + m_parse_error = true; + } } else { diff --git a/source/Target/FileAction.cpp b/source/Target/FileAction.cpp index 18b039998bc70..8611ff5f2d2cc 100644 --- a/source/Target/FileAction.cpp +++ b/source/Target/FileAction.cpp @@ -13,6 +13,7 @@ #include "lldb/Host/Windows/win32.h" // For O_NOCTTY #endif +#include "lldb/Core/Stream.h" #include "lldb/Target/FileAction.h" using namespace lldb_private; @@ -21,11 +22,11 @@ using namespace lldb_private; // FileAction member functions //---------------------------------------------------------------------------- -FileAction::FileAction() - : m_action(eFileActionNone) - , m_fd(-1) - , m_arg(-1) - , m_path() +FileAction::FileAction() : + m_action(eFileActionNone), + m_fd(-1), + m_arg(-1), + m_path() { } @@ -93,3 +94,24 @@ FileAction::Duplicate(int fd, int dup_fd) } return m_fd >= 0; } + +void +FileAction::Dump(Stream &stream) const +{ + stream.PutCString("file action: "); + switch (m_action) + { + case eFileActionClose: + stream.Printf("close fd %d", m_fd); + break; + case eFileActionDuplicate: + stream.Printf("duplicate fd %d to %d", m_fd, m_arg); + break; + case eFileActionNone: + stream.PutCString("no action"); + break; + case eFileActionOpen: + stream.Printf("open fd %d with '%s', OFLAGS = 0x%x", m_fd, m_path.c_str(), m_arg); + break; + } +} diff --git a/source/Target/InstrumentationRuntime.cpp b/source/Target/InstrumentationRuntime.cpp new file mode 100644 index 0000000000000..b3b2393b02346 --- /dev/null +++ b/source/Target/InstrumentationRuntime.cpp @@ -0,0 +1,48 @@ +//===-- InstrumentationRuntime.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-private.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/InstrumentationRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +void +InstrumentationRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list, lldb_private::Process *process, InstrumentationRuntimeCollection &runtimes) +{ + InstrumentationRuntimeCreateInstance create_callback = NULL; + InstrumentationRuntimeGetType get_type_callback; + for (uint32_t idx = 0; ; ++idx) + { + create_callback = PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(idx); + if (create_callback == NULL) + break; + get_type_callback = PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(idx); + InstrumentationRuntimeType type = get_type_callback(); + + InstrumentationRuntimeCollection::iterator pos; + pos = runtimes.find (type); + if (pos == runtimes.end()) { + runtimes[type] = create_callback(process->shared_from_this()); + } + } +} + +void +InstrumentationRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list) +{ +} + +bool +InstrumentationRuntime::IsActive() +{ + return false; +} diff --git a/source/Target/InstrumentationRuntimeStopInfo.cpp b/source/Target/InstrumentationRuntimeStopInfo.cpp new file mode 100644 index 0000000000000..cdbf93b5f7209 --- /dev/null +++ b/source/Target/InstrumentationRuntimeStopInfo.cpp @@ -0,0 +1,36 @@ +//===-- InstrumentationRuntimeStopInfo.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/Target/InstrumentationRuntimeStopInfo.h" + +#include "lldb/lldb-private.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/InstrumentationRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(Thread &thread, std::string description, StructuredData::ObjectSP additional_data) : + StopInfo(thread, 0) +{ + m_extended_info = additional_data; + m_description = description; +} + +const char * +InstrumentationRuntimeStopInfo::GetDescription () +{ + return m_description.c_str(); +} + +StopInfoSP +InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData (Thread &thread, std::string description, StructuredData::ObjectSP additionalData) +{ + return StopInfoSP(new InstrumentationRuntimeStopInfo(thread, description, additionalData)); +} diff --git a/source/Target/LanguageRuntime.cpp b/source/Target/LanguageRuntime.cpp index 9d48d8b2de7f4..358bffee3a4d4 100644 --- a/source/Target/LanguageRuntime.cpp +++ b/source/Target/LanguageRuntime.cpp @@ -10,6 +10,7 @@ #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/SearchFilter.h" using namespace lldb; using namespace lldb_private; @@ -19,17 +20,22 @@ class ExceptionSearchFilter : public SearchFilter { public: ExceptionSearchFilter (const lldb::TargetSP &target_sp, - lldb::LanguageType language) : + lldb::LanguageType language, + bool update_module_list = true) : SearchFilter (target_sp), m_language (language), m_language_runtime (NULL), m_filter_sp () { - UpdateModuleListIfNeeded (); + if (update_module_list) + UpdateModuleListIfNeeded (); } - - virtual bool - ModulePasses (const lldb::ModuleSP &module_sp) + + virtual + ~ExceptionSearchFilter() {}; + + bool + ModulePasses (const lldb::ModuleSP &module_sp) override { UpdateModuleListIfNeeded (); if (m_filter_sp) @@ -37,8 +43,8 @@ public: return false; } - virtual bool - ModulePasses (const FileSpec &spec) + bool + ModulePasses (const FileSpec &spec) override { UpdateModuleListIfNeeded (); if (m_filter_sp) @@ -47,16 +53,16 @@ public: } - virtual void - Search (Searcher &searcher) + void + Search (Searcher &searcher) override { UpdateModuleListIfNeeded (); if (m_filter_sp) m_filter_sp->Search (searcher); } - virtual void - GetDescription (Stream *s) + void + GetDescription (Stream *s) override { UpdateModuleListIfNeeded (); if (m_filter_sp) @@ -68,6 +74,12 @@ protected: LanguageRuntime *m_language_runtime; SearchFilterSP m_filter_sp; + SearchFilterSP + DoCopyForBreakpoint(Breakpoint &breakpoint) override + { + return SearchFilterSP(new ExceptionSearchFilter(TargetSP(), m_language, false)); + } + void UpdateModuleListIfNeeded () { @@ -124,11 +136,11 @@ public: { } - virtual Searcher::CallbackReturn + Searcher::CallbackReturn SearchCallback (SearchFilter &filter, SymbolContext &context, Address *addr, - bool containing) + bool containing) override { if (SetActualResolver()) @@ -137,8 +149,8 @@ public: return eCallbackReturnStop; } - virtual Searcher::Depth - GetDepth () + Searcher::Depth + GetDepth () override { if (SetActualResolver()) return m_actual_resolver_sp->GetDepth(); @@ -146,8 +158,8 @@ public: return eDepthTarget; } - virtual void - GetDescription (Stream *s) + void + GetDescription (Stream *s) override { s->Printf ("Exception breakpoint (catch: %s throw: %s)", m_catch_bp ? "on" : "off", @@ -163,8 +175,8 @@ public: s->Printf (" the correct runtime exception handler will be determined when you run"); } - virtual void - Dump (Stream *s) const + void + Dump (Stream *s) const override { } @@ -174,6 +186,12 @@ public: return V->getResolverID() == BreakpointResolver::ExceptionResolver; } protected: + BreakpointResolverSP + CopyForBreakpoint (Breakpoint &breakpoint) override + { + return BreakpointResolverSP(new ExceptionBreakpointResolver(m_language, m_catch_bp, m_throw_bp)); + } + bool SetActualResolver() { diff --git a/source/Target/Memory.cpp b/source/Target/Memory.cpp index b212fcd23a453..934bc967b68b6 100644 --- a/source/Target/Memory.cpp +++ b/source/Target/Memory.cpp @@ -26,7 +26,7 @@ using namespace lldb_private; //---------------------------------------------------------------------- MemoryCache::MemoryCache(Process &process) : m_process (process), - m_cache_line_byte_size (512), + m_cache_line_byte_size (process.GetMemoryCacheLineSize()), m_mutex (Mutex::eMutexTypeRecursive), m_cache (), m_invalid_ranges () @@ -47,6 +47,7 @@ MemoryCache::Clear(bool clear_invalid_ranges) m_cache.clear(); if (clear_invalid_ranges) m_invalid_ranges.Clear(); + m_cache_line_byte_size = m_process.GetMemoryCacheLineSize(); } void @@ -250,7 +251,8 @@ AllocatedBlock::ReserveBlock (uint32_t size) { m_offset_to_chunk_size[0] = needed_chunks; if (log) - log->Printf ("[1] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", this, size, size, 0, needed_chunks, m_chunk_size); + log->Printf("[1] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", (void *)this, + size, size, 0, needed_chunks, m_chunk_size); addr = m_addr; } else @@ -268,7 +270,9 @@ AllocatedBlock::ReserveBlock (uint32_t size) { m_offset_to_chunk_size[last_offset] = needed_chunks; if (log) - log->Printf ("[2] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - num_chunks %lu", this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); + log->Printf("[2] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - " + "num_chunks %lu", + (void *)this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); addr = m_addr + last_offset; break; } @@ -284,7 +288,9 @@ AllocatedBlock::ReserveBlock (uint32_t size) { m_offset_to_chunk_size[last_offset] = needed_chunks; if (log) - log->Printf ("[3] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - num_chunks %lu", this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); + log->Printf("[3] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - " + "num_chunks %lu", + (void *)this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); addr = m_addr + last_offset; break; } @@ -345,7 +351,7 @@ AllocatedBlock::ReserveBlock (uint32_t size) } if (log) - log->Printf ("AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => 0x%16.16" PRIx64, this, size, size, (uint64_t)addr); + log->Printf("AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => 0x%16.16" PRIx64, (void *)this, size, size, (uint64_t)addr); return addr; } @@ -362,7 +368,8 @@ AllocatedBlock::FreeBlock (addr_t addr) } Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); if (log) - log->Printf ("AllocatedBlock::FreeBlock(%p) (addr = 0x%16.16" PRIx64 ") => %i, num_chunks: %lu", this, (uint64_t)addr, success, m_offset_to_chunk_size.size()); + log->Printf("AllocatedBlock::FreeBlock(%p) (addr = 0x%16.16" PRIx64 ") => %i, num_chunks: %lu", (void *)this, (uint64_t)addr, + success, m_offset_to_chunk_size.size()); return success; } diff --git a/source/Target/MemoryHistory.cpp b/source/Target/MemoryHistory.cpp new file mode 100644 index 0000000000000..b97096b06d762 --- /dev/null +++ b/source/Target/MemoryHistory.cpp @@ -0,0 +1,30 @@ +//===-- MemoryHistory.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/Target/MemoryHistory.h" + +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::MemoryHistorySP +MemoryHistory::FindPlugin (const ProcessSP process) +{ + MemoryHistoryCreateInstance create_callback = NULL; + + for (uint32_t idx = 0; (create_callback = PluginManager::GetMemoryHistoryCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + MemoryHistorySP memory_history_sp (create_callback (process)); + if (memory_history_sp.get()) + return memory_history_sp; + } + + return MemoryHistorySP(); +} diff --git a/source/Target/ObjCLanguageRuntime.cpp b/source/Target/ObjCLanguageRuntime.cpp index 1f2abd876c840..b182407bf68ec 100644 --- a/source/Target/ObjCLanguageRuntime.cpp +++ b/source/Target/ObjCLanguageRuntime.cpp @@ -603,20 +603,20 @@ ObjCLanguageRuntime::GetNonKVOClassDescriptor (ObjCISA isa) ClangASTType -ObjCLanguageRuntime::EncodingToType::RealizeType (const char* name, bool allow_unknownanytype) +ObjCLanguageRuntime::EncodingToType::RealizeType (const char* name, bool for_expression) { if (m_scratch_ast_ctx_ap) - return RealizeType(*m_scratch_ast_ctx_ap, name, allow_unknownanytype); + return RealizeType(*m_scratch_ast_ctx_ap, name, for_expression); return ClangASTType(); } ClangASTType -ObjCLanguageRuntime::EncodingToType::RealizeType (ClangASTContext& ast_ctx, const char* name, bool allow_unknownanytype) +ObjCLanguageRuntime::EncodingToType::RealizeType (ClangASTContext& ast_ctx, const char* name, bool for_expression) { clang::ASTContext *clang_ast = ast_ctx.getASTContext(); if (!clang_ast) return ClangASTType(); - return RealizeType(*clang_ast, name, allow_unknownanytype); + return RealizeType(*clang_ast, name, for_expression); } ObjCLanguageRuntime::EncodingToType::~EncodingToType() {} diff --git a/source/Target/Platform.cpp b/source/Target/Platform.cpp index fe73be2d05b91..4ebd18b8bfd09 100644 --- a/source/Target/Platform.cpp +++ b/source/Target/Platform.cpp @@ -32,26 +32,12 @@ using namespace lldb_private; // Use a singleton function for g_local_platform_sp to avoid init // constructors since LLDB is often part of a shared library static PlatformSP& -GetDefaultPlatformSP () +GetHostPlatformSP () { - static PlatformSP g_default_platform_sp; - return g_default_platform_sp; + static PlatformSP g_platform_sp; + return g_platform_sp; } -static Mutex & -GetConnectedPlatformListMutex () -{ - static Mutex g_remote_connected_platforms_mutex (Mutex::eMutexTypeRecursive); - return g_remote_connected_platforms_mutex; -} -static std::vector<PlatformSP> & -GetConnectedPlatformList () -{ - static std::vector<PlatformSP> g_remote_connected_platforms; - return g_remote_connected_platforms; -} - - const char * Platform::GetHostPlatformName () { @@ -69,17 +55,37 @@ Platform::GetHostPlatformName () /// or attaching to processes unless another platform is specified. //------------------------------------------------------------------ PlatformSP -Platform::GetDefaultPlatform () +Platform::GetHostPlatform () { - return GetDefaultPlatformSP (); + return GetHostPlatformSP (); +} + +static std::vector<PlatformSP> & +GetPlatformList() +{ + static std::vector<PlatformSP> g_platform_list; + return g_platform_list; +} + +static Mutex & +GetPlatformListMutex () +{ + static Mutex g_mutex(Mutex::eMutexTypeRecursive); + return g_mutex; } void -Platform::SetDefaultPlatform (const lldb::PlatformSP &platform_sp) +Platform::SetHostPlatform (const lldb::PlatformSP &platform_sp) { // The native platform should use its static void Platform::Initialize() // function to register itself as the native platform. - GetDefaultPlatformSP () = platform_sp; + GetHostPlatformSP () = platform_sp; + + if (platform_sp) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); + } } Error @@ -98,36 +104,36 @@ Platform::LocateExecutableScriptingResources (Target *target, Module &module, St return FileSpecList(); } -Platform* -Platform::FindPlugin (Process *process, const ConstString &plugin_name) -{ - PlatformCreateInstance create_callback = NULL; - if (plugin_name) - { - create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); - if (create_callback) - { - ArchSpec arch; - if (process) - { - arch = process->GetTarget().GetArchitecture(); - } - std::unique_ptr<Platform> instance_ap(create_callback(process, &arch)); - if (instance_ap.get()) - return instance_ap.release(); - } - } - else - { - for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx) - { - std::unique_ptr<Platform> instance_ap(create_callback(process, nullptr)); - if (instance_ap.get()) - return instance_ap.release(); - } - } - return NULL; -} +//PlatformSP +//Platform::FindPlugin (Process *process, const ConstString &plugin_name) +//{ +// PlatformCreateInstance create_callback = NULL; +// if (plugin_name) +// { +// create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); +// if (create_callback) +// { +// ArchSpec arch; +// if (process) +// { +// arch = process->GetTarget().GetArchitecture(); +// } +// PlatformSP platform_sp(create_callback(process, &arch)); +// if (platform_sp) +// return platform_sp; +// } +// } +// else +// { +// for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx) +// { +// PlatformSP platform_sp(create_callback(process, nullptr)); +// if (platform_sp) +// return platform_sp; +// } +// } +// return PlatformSP(); +//} Error Platform::GetSharedModule (const ModuleSpec &module_spec, @@ -153,21 +159,50 @@ Platform::GetSharedModule (const ModuleSpec &module_spec, } PlatformSP -Platform::Create (const char *platform_name, Error &error) +Platform::Find (const ConstString &name) +{ + if (name) + { + static ConstString g_host_platform_name ("host"); + if (name == g_host_platform_name) + return GetHostPlatform(); + + Mutex::Locker locker(GetPlatformListMutex ()); + for (const auto &platform_sp : GetPlatformList()) + { + if (platform_sp->GetName() == name) + return platform_sp; + } + } + return PlatformSP(); +} + +PlatformSP +Platform::Create (const ConstString &name, Error &error) { PlatformCreateInstance create_callback = NULL; lldb::PlatformSP platform_sp; - if (platform_name && platform_name[0]) + if (name) { - ConstString const_platform_name (platform_name); - create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (const_platform_name); + static ConstString g_host_platform_name ("host"); + if (name == g_host_platform_name) + return GetHostPlatform(); + + create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (name); if (create_callback) - platform_sp.reset(create_callback(true, NULL)); + platform_sp = create_callback(true, NULL); else - error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", platform_name); + error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", name.GetCString()); } else error.SetErrorString ("invalid platform name"); + + if (platform_sp) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); + } + return platform_sp; } @@ -178,28 +213,52 @@ Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &erro lldb::PlatformSP platform_sp; if (arch.IsValid()) { - uint32_t idx; + // Scope for locker + { + // First try exact arch matches across all platforms already created + Mutex::Locker locker(GetPlatformListMutex ()); + for (const auto &platform_sp : GetPlatformList()) + { + if (platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) + return platform_sp; + } + + // Next try compatible arch matches across all platforms already created + for (const auto &platform_sp : GetPlatformList()) + { + if (platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) + return platform_sp; + } + } + PlatformCreateInstance create_callback; // First try exact arch matches across all platform plug-ins - bool exact = true; + uint32_t idx; for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx) { if (create_callback) { - platform_sp.reset(create_callback(false, &arch)); - if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, exact, platform_arch_ptr)) + platform_sp = create_callback(false, &arch); + if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); return platform_sp; + } } } // Next try compatible arch matches across all platform plug-ins - exact = false; for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx) { if (create_callback) { - platform_sp.reset(create_callback(false, &arch)); - if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, exact, platform_arch_ptr)) + platform_sp = create_callback(false, &arch); + if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); return platform_sp; + } } } } @@ -211,25 +270,6 @@ Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &erro return platform_sp; } -uint32_t -Platform::GetNumConnectedRemotePlatforms () -{ - Mutex::Locker locker (GetConnectedPlatformListMutex ()); - return GetConnectedPlatformList().size(); -} - -PlatformSP -Platform::GetConnectedRemotePlatformAtIndex (uint32_t idx) -{ - PlatformSP platform_sp; - { - Mutex::Locker locker (GetConnectedPlatformListMutex ()); - if (idx < GetConnectedPlatformList().size()) - platform_sp = GetConnectedPlatformList ()[idx]; - } - return platform_sp; -} - //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ @@ -422,6 +462,20 @@ Platform::GetOSKernelDescription (std::string &s) return GetRemoteOSKernelDescription (s); } +void +Platform::AddClangModuleCompilationOptions (std::vector<std::string> &options) +{ + std::vector<std::string> default_compilation_options = + { + "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc" + }; + + options.insert(options.end(), + default_compilation_options.begin(), + default_compilation_options.end()); +} + + ConstString Platform::GetWorkingDirectory () { @@ -537,11 +591,11 @@ RecurseCopy_Callback (void *baton, case FileSpec::eFileTypeInvalid: case FileSpec::eFileTypeOther: case FileSpec::eFileTypeUnknown: - default: rc_baton->error.SetErrorStringWithFormat("invalid file detected during copy: %s", src.GetPath().c_str()); return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out break; } + llvm_unreachable("Unhandled FileSpec::FileType!"); } Error @@ -842,15 +896,13 @@ Platform::SetOSVersion (uint32_t major, Error -Platform::ResolveExecutable (const FileSpec &exe_file, - const ArchSpec &exe_arch, +Platform::ResolveExecutable (const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Error error; - if (exe_file.Exists()) + if (module_spec.GetFileSpec().Exists()) { - ModuleSpec module_spec (exe_file, exe_arch); if (module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule (module_spec, @@ -864,9 +916,10 @@ Platform::ResolveExecutable (const FileSpec &exe_file, // No valid architecture was specified, ask the platform for // the architectures that we should be using (in the correct order) // and see if we can find a match that way - for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, module_spec.GetArchitecture()); ++idx) + ModuleSpec arch_module_spec(module_spec); + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, arch_module_spec.GetArchitecture()); ++idx) { - error = ModuleList::GetSharedModule (module_spec, + error = ModuleList::GetSharedModule (arch_module_spec, exe_module_sp, module_search_paths_ptr, NULL, @@ -880,7 +933,7 @@ Platform::ResolveExecutable (const FileSpec &exe_file, else { error.SetErrorStringWithFormat ("'%s' does not exist", - exe_file.GetPath().c_str()); + module_spec.GetFileSpec().GetPath().c_str()); } return error; } @@ -1005,7 +1058,11 @@ Error Platform::LaunchProcess (ProcessLaunchInfo &launch_info) { Error error; - // Take care of the host case so that each subclass can just + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("Platform::%s()", __FUNCTION__); + + // Take care of the host case so that each subclass can just // call this function to get the host functionality. if (IsHost()) { @@ -1018,6 +1075,16 @@ Platform::LaunchProcess (ProcessLaunchInfo &launch_info) const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug); const bool first_arg_is_full_shell_command = false; uint32_t num_resumes = GetResumeCountForLaunchInfo (launch_info); + if (log) + { + const FileSpec &shell = launch_info.GetShell(); + const char *shell_str = (shell) ? shell.GetPath().c_str() : "<null>"; + log->Printf ("Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 ", shell is '%s'", + __FUNCTION__, + num_resumes, + shell_str); + } + if (!launch_info.ConvertArgumentsForLaunchingInShell (error, is_localhost, will_debug, @@ -1026,6 +1093,9 @@ Platform::LaunchProcess (ProcessLaunchInfo &launch_info) return error; } + if (log) + log->Printf ("Platform::%s final launch_info resume count: %" PRIu32, __FUNCTION__, launch_info.GetResumeCount ()); + error = Host::LaunchProcess (launch_info); } else @@ -1037,9 +1107,12 @@ lldb::ProcessSP Platform::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one - Listener &listener, Error &error) { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("Platform::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target)); + ProcessSP process_sp; // Make sure we stop at the entry point launch_info.GetFlags ().Set (eLaunchFlagDebug); @@ -1051,12 +1124,16 @@ Platform::DebugProcess (ProcessLaunchInfo &launch_info, error = LaunchProcess (launch_info); if (error.Success()) { + if (log) + log->Printf ("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", __FUNCTION__, launch_info.GetProcessID ()); if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info (launch_info); - process_sp = Attach (attach_info, debugger, target, listener, error); + process_sp = Attach (attach_info, debugger, target, error); if (process_sp) { + if (log) + log->Printf ("Platform::%s Attach() succeeded, Process plugin: %s", __FUNCTION__, process_sp->GetPluginName ().AsCString ()); launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach @@ -1075,8 +1152,24 @@ Platform::DebugProcess (ProcessLaunchInfo &launch_info, process_sp->SetSTDIOFileDescriptor(pty_fd); } } + else + { + if (log) + log->Printf ("Platform::%s Attach() failed: %s", __FUNCTION__, error.AsCString ()); + } } + else + { + if (log) + log->Printf ("Platform::%s LaunchProcess() returned launch_info with invalid process id", __FUNCTION__); + } + } + else + { + if (log) + log->Printf ("Platform::%s LaunchProcess() failed: %s", __FUNCTION__, error.AsCString ()); } + return process_sp; } diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index a1049787d8210..106678da2684e 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -16,22 +16,25 @@ #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Event.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" -#include "lldb/Symbol/Symbol.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Expression/ClangUserExpression.h" -#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Host/Pipe.h" #include "lldb/Host/Terminal.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/JITLoader.h" +#include "lldb/Target/MemoryHistory.h" #include "lldb/Target/OperatingSystem.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/CPPLanguageRuntime.h" @@ -45,6 +48,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/InstrumentationRuntime.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" using namespace lldb; @@ -108,6 +112,7 @@ g_properties[] = { "python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, NULL, NULL, "A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class." }, { "stop-on-sharedlibrary-events" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, stop when a shared library is loaded or unloaded." }, { "detach-keeps-stopped" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, detach will attempt to keep the process stopped." }, + { "memory-cache-line-size" , OptionValue::eTypeUInt64, false, 512, NULL, NULL, "The memory cache line size" }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; @@ -118,14 +123,17 @@ enum { ePropertyUnwindOnErrorInExpressions, ePropertyPythonOSPluginPath, ePropertyStopOnSharedLibraryEvents, - ePropertyDetachKeepsStopped + ePropertyDetachKeepsStopped, + ePropertyMemCacheLineSize }; -ProcessProperties::ProcessProperties (bool is_global) : - Properties () +ProcessProperties::ProcessProperties (lldb_private::Process *process) : + Properties (), + m_process (process) // Can be NULL for global ProcessProperties { - if (is_global) + if (process == NULL) { + // Global process properties, set them up one time m_collection_sp.reset (new ProcessOptionValueProperties(ConstString("process"))); m_collection_sp->Initialize(g_properties); m_collection_sp->AppendProperty(ConstString("thread"), @@ -134,13 +142,24 @@ ProcessProperties::ProcessProperties (bool is_global) : Thread::GetGlobalProperties()->GetValueProperties()); } else + { m_collection_sp.reset (new ProcessOptionValueProperties(Process::GetGlobalProperties().get())); + m_collection_sp->SetValueChangedCallback(ePropertyPythonOSPluginPath, ProcessProperties::OptionValueChangedCallback, this); + } } ProcessProperties::~ProcessProperties() { } +void +ProcessProperties::OptionValueChangedCallback (void *baton, OptionValue *option_value) +{ + ProcessProperties *properties = (ProcessProperties *)baton; + if (properties->m_process) + properties->m_process->LoadOperatingSystemPlugin(true); +} + bool ProcessProperties::GetDisableMemoryCache() const { @@ -148,6 +167,13 @@ ProcessProperties::GetDisableMemoryCache() const return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } +uint64_t +ProcessProperties::GetMemoryCacheLineSize() const +{ + const uint32_t idx = ePropertyMemCacheLineSize; + return m_collection_sp->GetPropertyAtIndexAsUInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + Args ProcessProperties::GetExtraStartupCommands () const { @@ -467,9 +493,9 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op case 'c': if (option_arg && option_arg[0]) - launch_info.SetShell (option_arg); + launch_info.SetShell (FileSpec(option_arg, false)); else - launch_info.SetShell (LLDB_DEFAULT_SHELL); + launch_info.SetShell (HostInfo::GetDefaultShell()); break; case 'v': @@ -660,7 +686,7 @@ Process::Process(Target &target, Listener &listener) : } Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_signals_sp) : - ProcessProperties (false), + ProcessProperties (this), UserID (LLDB_INVALID_PROCESS_ID), Broadcaster (&(target.GetDebugger()), "lldb.process"), m_target (target), @@ -670,13 +696,13 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_private_state_control_broadcaster (NULL, "lldb.process.internal_state_control_broadcaster"), m_private_state_listener ("lldb.process.internal_state_listener"), m_private_state_control_wait(), - m_private_state_thread (LLDB_INVALID_HOST_THREAD), m_mod_id (), m_process_unique_id(0), m_thread_index_id (0), m_thread_id_to_index_id_map (), m_exit_status (-1), m_exit_string (), + m_exit_status_mutex(), m_thread_mutex (Mutex::eMutexTypeRecursive), m_thread_list_real (this), m_thread_list (this), @@ -706,6 +732,7 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_public_run_lock (), m_private_run_lock (), m_currently_handling_event(false), + m_stop_info_override_callback (NULL), m_finalize_called(false), m_clear_thread_plans_on_stop (false), m_force_next_event_delivery(false), @@ -760,6 +787,11 @@ Process::~Process() if (log) log->Printf ("%p Process::~Process()", static_cast<void*>(this)); StopPrivateStateThread(); + + // ThreadList::Clear() will try to acquire this process's mutex, so + // explicitly clear the thread list here to ensure that the mutex + // is not destroyed before the thread list. + m_thread_list.Clear(); } const ProcessPropertiesSP & @@ -767,7 +799,7 @@ Process::GetGlobalProperties() { static ProcessPropertiesSP g_settings_sp; if (!g_settings_sp) - g_settings_sp.reset (new ProcessProperties (true)); + g_settings_sp.reset (new ProcessProperties (NULL)); return g_settings_sp; } @@ -826,7 +858,9 @@ Process::Finalize() m_memory_cache.Clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_instrumentation_runtimes.clear(); m_next_event_action_ap.reset(); + m_stop_info_override_callback = NULL; //#ifdef LLDB_CONFIGURATION_DEBUG // StreamFile s(stdout, false); // EventSP event_sp; @@ -924,7 +958,7 @@ Process::SyncIOHandler (uint64_t timeout_msec) log->Printf ("Process::%s pid %" PRIu64 ": SUCCESS", __FUNCTION__, GetID ()); } - // reset sync one-shot so it will be ready for next time + // reset sync one-shot so it will be ready for next launch m_iohandler_sync.SetValue(false, eBroadcastNever); } @@ -932,7 +966,11 @@ Process::SyncIOHandler (uint64_t timeout_msec) } StateType -Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr, bool wait_always, Listener *hijack_listener) +Process::WaitForProcessToStop (const TimeValue *timeout, + EventSP *event_sp_ptr, + bool wait_always, + Listener *hijack_listener, + Stream *stream) { // We can't just wait for a "stopped" event, because the stopped event may have restarted the target. // We have to actually check each event, and in the case of a stopped event check the restarted flag @@ -966,6 +1004,9 @@ Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp if (event_sp_ptr && event_sp) *event_sp_ptr = event_sp; + bool pop_process_io_handler = hijack_listener != NULL; + Process::HandleProcessStateChangedEvent (event_sp, stream, pop_process_io_handler); + switch (state) { case eStateCrashed: @@ -995,6 +1036,195 @@ Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp return state; } +bool +Process::HandleProcessStateChangedEvent (const EventSP &event_sp, + Stream *stream, + bool &pop_process_io_handler) +{ + const bool handle_pop = pop_process_io_handler == true; + + pop_process_io_handler = false; + ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + if (!process_sp) + return false; + + StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + if (event_state == eStateInvalid) + return false; + + switch (event_state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + { + if (stream) + stream->Printf ("Process %" PRIu64 " %s\n", + process_sp->GetID(), + StateAsCString (event_state)); + + if (event_state == eStateDetached) + pop_process_io_handler = true; + } + break; + + case eStateConnected: + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + if (stream) + process_sp->GetStatus(*stream); + pop_process_io_handler = true; + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) + { + if (stream) + { + 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->Printf ("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : "<UNKNOWN REASON>"); + } + else + { + stream->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->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>"); + } + } + } + } + } + else + { + // Lock the thread list so it doesn't change on us, this is the scope for the locker: + { + 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: + case eStopReasonInstrumentation: + 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()); + } + } + } + // Drop the ThreadList mutex by here, since GetThreadStatus below might have to run code, + // e.g. for Data formatters, and if we hold the ThreadList mutex, then the process is going to + // have a hard time restarting the process. + if (stream) + { + Debugger &debugger = process_sp->GetTarget().GetDebugger(); + if (debugger.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); + process_sp->GetThreadStatus (*stream, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + } + else + { + uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream->Printf ("Target %d: (", target_idx); + else + stream->Printf ("Target <unknown index>: ("); + process_sp->GetTarget().Dump (stream, eDescriptionLevelBrief); + stream->Printf (") stopped.\n"); + } + } + + // Pop the process IO handler + pop_process_io_handler = true; + } + break; + } + + if (handle_pop && pop_process_io_handler) + process_sp->PopProcessIOHandler(); + + return true; +} + StateType Process::WaitForState @@ -1170,6 +1400,8 @@ Process::IsRunning () const int Process::GetExitStatus () { + Mutex::Locker locker (m_exit_status_mutex); + if (m_public_state.GetValue() == eStateExited) return m_exit_status; return -1; @@ -1179,6 +1411,8 @@ Process::GetExitStatus () const char * Process::GetExitDescription () { + Mutex::Locker locker (m_exit_status_mutex); + if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) return m_exit_string.c_str(); return NULL; @@ -1203,11 +1437,16 @@ Process::SetExitStatus (int status, const char *cstr) return false; } - m_exit_status = status; - if (cstr) - m_exit_string = cstr; - else - m_exit_string.clear(); + // use a mutex to protect the status and string during updating + { + Mutex::Locker locker (m_exit_status_mutex); + + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + } DidExit (); @@ -1392,6 +1631,17 @@ Process::GetState() return m_public_state.GetValue (); } +bool +Process::StateChangedIsExternallyHijacked() +{ + if (IsHijackedForEvent(eBroadcastBitStateChanged)) + { + if (strcmp(m_hijacking_listeners.back()->GetName(), "lldb.Process.ResumeSynchronous.hijack")) + return true; + } + return false; +} + void Process::SetPublicState (StateType new_state, bool restarted) { @@ -1404,7 +1654,7 @@ Process::SetPublicState (StateType new_state, bool restarted) // On the transition from Run to Stopped, we unlock the writer end of the // run lock. The lock gets locked in Resume, which is the public API // to tell the program to run. - if (!IsHijackedForEvent(eBroadcastBitStateChanged)) + if (!StateChangedIsExternallyHijacked()) { if (new_state == eStateDetached) { @@ -1445,6 +1695,36 @@ Process::Resume () return PrivateResume(); } +Error +Process::ResumeSynchronous (Stream *stream) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("Process::ResumeSynchronous -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) + { + Error error("Resume request failed - process still running."); + if (log) + log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + + ListenerSP listener_sp (new Listener("lldb.Process.ResumeSynchronous.hijack")); + HijackProcessEvents(listener_sp.get()); + + Error error = PrivateResume(); + + StateType state = WaitForProcessToStop (NULL, NULL, true, listener_sp.get(), stream); + + // Undo the hijacking of process events... + RestoreProcessEvents(); + + if (error.Success() && !StateIsStoppedState(state, false)) + error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); + + return error; +} + StateType Process::GetPrivateState () { @@ -1617,12 +1897,17 @@ Process::LoadImage (const FileSpec &image_spec, Error &error) { if (error_str_sp->IsCStringContainer(true)) { - StreamString s; - size_t num_chars = error_str_sp->ReadPointedString (s, error); + DataBufferSP buffer_sp(new DataBufferHeap(10240,0)); + size_t num_chars = error_str_sp->ReadPointedString (buffer_sp, error, 10240); if (error.Success() && num_chars > 0) { error.Clear(); - error.SetErrorStringWithFormat("dlopen error: %s", s.GetData()); + error.SetErrorStringWithFormat("dlopen error: %s", buffer_sp->GetBytes()); + } + else + { + error.Clear(); + error.SetErrorStringWithFormat("dlopen failed for unknown reasons."); } } } @@ -2507,10 +2792,10 @@ Process::WriteMemory (addr_t addr, const void *buf, size_t size, Error &error) }); if (bytes_written < size) - bytes_written += WriteMemoryPrivate (addr + bytes_written, - ubuf + bytes_written, - size - bytes_written, - error); + WriteMemoryPrivate (addr + bytes_written, + ubuf + bytes_written, + size - bytes_written, + error); } } else @@ -2722,6 +3007,16 @@ Process::WaitForProcessStopPrivate (const TimeValue *timeout, EventSP &event_sp) return state; } +void +Process::LoadOperatingSystemPlugin(bool flush) +{ + if (flush) + m_thread_list.Clear(); + m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); + if (flush) + Flush(); +} + Error Process::Launch (ProcessLaunchInfo &launch_info) { @@ -2732,6 +3027,7 @@ Process::Launch (ProcessLaunchInfo &launch_info) m_system_runtime_ap.reset(); m_os_ap.reset(); m_process_input_reader.reset(); + m_stop_info_override_callback = NULL; Module *exe_module = m_target.GetExecutableModulePointer(); if (exe_module) @@ -2811,15 +3107,20 @@ Process::Launch (ProcessLaunchInfo &launch_info) if (system_runtime) system_runtime->DidLaunch(); - m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); - // This delays passing the stopped event to listeners till DidLaunch gets - // a chance to complete... - HandlePrivateEvent (event_sp); + LoadOperatingSystemPlugin(false); + + // Note, the stop event was consumed above, but not handled. This was done + // to give DidLaunch a chance to run. The target is either stopped or crashed. + // Directly set the state. This is done to prevent a stop message with a bunch + // of spurious output on thread status, as well as not pop a ProcessIOHandler. + SetPublicState(state, false); if (PrivateStateThreadIsValid ()) ResumePrivateStateThread (); else StartPrivateStateThread (); + + m_stop_info_override_callback = GetTarget().GetArchitecture().GetStopInfoOverrideCallback(); } else if (state == eStateExited) { @@ -2979,6 +3280,15 @@ Process::AttachCompletionHandler::GetExitString () return m_exit_string.c_str(); } +Listener & +ProcessAttachInfo::GetListenerForProcess (Debugger &debugger) +{ + if (m_listener_sp) + return *m_listener_sp; + else + return debugger.GetListener(); +} + Error Process::Attach (ProcessAttachInfo &attach_info) { @@ -2988,6 +3298,7 @@ Process::Attach (ProcessAttachInfo &attach_info) m_jit_loaders_ap.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); + m_stop_info_override_callback = NULL; lldb::pid_t attach_pid = attach_info.GetProcessID(); Error error; @@ -3115,14 +3426,13 @@ Process::Attach (ProcessAttachInfo &attach_info) else { if (GetID() != LLDB_INVALID_PROCESS_ID) - { SetID (LLDB_INVALID_PROCESS_ID); - const char *error_string = error.AsCString(); - if (error_string == NULL) - error_string = "attach failed"; - SetExitStatus(-1, error_string); - } + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); } } } @@ -3246,6 +3556,8 @@ Process::CompleteAttach () exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "<none>"); } } + + m_stop_info_override_callback = process_arch.GetStopInfoOverrideCallback(); } Error @@ -3823,26 +4135,25 @@ Process::StartPrivateStateThread (bool force) // events make it to clients (into the DCProcess event queue). char thread_name[1024]; - if (Host::MAX_THREAD_NAME_LENGTH <= 16) + if (HostInfo::GetMaxThreadNameLength() <= 30) { - // On platforms with abbreviated thread name lengths, choose thread names that fit within the limit. - if (already_running) - snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); - else - snprintf(thread_name, sizeof(thread_name), "intern-state"); + // On platforms with abbreviated thread name lengths, choose thread names that fit within the limit. + if (already_running) + snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); + else + snprintf(thread_name, sizeof(thread_name), "intern-state"); } else { if (already_running) - snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state-override(pid=%" PRIu64 ")>", GetID()); + snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state-override(pid=%" PRIu64 ")>", GetID()); else - snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID()); + snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID()); } // Create the private state thread, and start it running. - m_private_state_thread = Host::ThreadCreate (thread_name, Process::PrivateStateThread, this, NULL); - bool success = IS_VALID_LLDB_HOST_THREAD(m_private_state_thread); - if (success) + m_private_state_thread = ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, this, NULL); + if (m_private_state_thread.IsJoinable()) { ResumePrivateStateThread(); return true; @@ -3891,8 +4202,8 @@ Process::ControlPrivateStateThread (uint32_t signal) // Signal the private state thread. First we should copy this is case the // thread starts exiting since the private state thread will NULL this out // when it exits - const lldb::thread_t private_state_thread = m_private_state_thread; - if (IS_VALID_LLDB_HOST_THREAD(private_state_thread)) + HostThread private_state_thread(m_private_state_thread); + if (private_state_thread.IsJoinable()) { TimeValue timeout_time; bool timed_out; @@ -3910,8 +4221,7 @@ Process::ControlPrivateStateThread (uint32_t signal) { if (timed_out) { - Error error; - Host::ThreadCancel (private_state_thread, &error); + Error error = private_state_thread.Cancel(); if (log) log->Printf ("Timed out responding to the control event, cancel got error: \"%s\".", error.AsCString()); } @@ -3922,8 +4232,8 @@ Process::ControlPrivateStateThread (uint32_t signal) } thread_result_t result = NULL; - Host::ThreadJoin (private_state_thread, &result, NULL); - m_private_state_thread = LLDB_INVALID_HOST_THREAD; + private_state_thread.Join(&result); + m_private_state_thread.Reset(); } } else @@ -4005,7 +4315,8 @@ Process::HandlePrivateEvent (EventSP &event_sp) { // Only push the input handler if we aren't fowarding events, // as this means the curses GUI is in use... - if (!GetTarget().GetDebugger().IsForwardingEvents()) + // Or don't push it if we are launching since it will come up stopped. + if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching) PushProcessIOHandler (); m_iohandler_sync.SetValue(true, eBroadcastAlways); } @@ -4167,7 +4478,7 @@ Process::RunPrivateStateThread () m_public_run_lock.SetStopped(); m_private_state_control_wait.SetValue (true, eBroadcastAlways); - m_private_state_thread = LLDB_INVALID_HOST_THREAD; + m_private_state_thread.Reset(); return NULL; } @@ -4621,7 +4932,7 @@ class IOHandlerProcessSTDIO : public: IOHandlerProcessSTDIO (Process *process, int write_fd) : - IOHandler(process->GetTarget().GetDebugger()), + IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO), m_process (process), m_read_file (), m_write_file (write_fd, false), @@ -4639,9 +4950,10 @@ public: bool OpenPipes () { - if (m_pipe.IsValid()) + if (m_pipe.CanRead() && m_pipe.CanWrite()) return true; - return m_pipe.Open(); + Error result = m_pipe.CreateNew(false); + return result.Success(); } void @@ -4702,8 +5014,10 @@ public: } if (FD_ISSET (pipe_read_fd, &read_fdset)) { + size_t bytes_read; // Consume the interrupt byte - if (m_pipe.Read (&ch, 1) == 1) + Error error = m_pipe.Read(&ch, 1, bytes_read); + if (error.Success()) { switch (ch) { @@ -4751,7 +5065,8 @@ public: Cancel () { char ch = 'q'; // Send 'q' for quit - m_pipe.Write (&ch, 1); + size_t bytes_written = 0; + m_pipe.Write(&ch, 1, bytes_written); } virtual bool @@ -4765,7 +5080,9 @@ public: if (m_active) { char ch = 'i'; // Send 'i' for interrupt - return m_pipe.Write (&ch, 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write(&ch, 1, bytes_written); + return result.Success(); } else { @@ -4944,12 +5261,12 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, selected_tid = LLDB_INVALID_THREAD_ID; } - lldb::thread_t backup_private_state_thread = LLDB_INVALID_HOST_THREAD; - lldb::StateType old_state; + HostThread backup_private_state_thread; + lldb::StateType old_state = eStateInvalid; lldb::ThreadPlanSP stopper_base_plan_sp; Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); - if (Host::GetCurrentThread() == m_private_state_thread) + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) { // Yikes, we are running on the private state thread! So we can't wait for public events on this thread, since // we are the thread that is generating public events. @@ -5554,7 +5871,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, } // END WAIT LOOP // If we had to start up a temporary private state thread to run this thread plan, shut it down now. - if (IS_VALID_LLDB_HOST_THREAD(backup_private_state_thread)) + if (backup_private_state_thread.IsJoinable()) { StopPrivateStateThread(); Error error; @@ -5563,8 +5880,8 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, { thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp); } - m_public_state.SetValueNoLock(old_state); - + if (old_state != eStateInvalid) + m_public_state.SetValueNoLock(old_state); } // Restore the thread state if we are going to discard the plan execution. There are three cases where this @@ -5848,21 +6165,21 @@ Process::GetThreadStatus (Stream &strm, // ID's, and look them up one by one: uint32_t num_threads; - std::vector<uint32_t> thread_index_array; + std::vector<lldb::tid_t> thread_id_array; //Scope for thread list locker; { Mutex::Locker locker (GetThreadList().GetMutex()); ThreadList &curr_thread_list = GetThreadList(); num_threads = curr_thread_list.GetSize(); uint32_t idx; - thread_index_array.resize(num_threads); + thread_id_array.resize(num_threads); for (idx = 0; idx < num_threads; ++idx) - thread_index_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); + thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); } for (uint32_t i = 0; i < num_threads; i++) { - ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_index_array[i])); + ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i])); if (thread_sp) { if (only_threads_with_stop_reason) @@ -5915,7 +6232,8 @@ Process::RunPreResumeActions () struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back(); m_pre_resume_actions.pop_back(); bool this_result = action.callback (action.baton); - if (result == true) result = this_result; + if (result == true) + result = this_result; } return result; } @@ -5955,8 +6273,10 @@ Process::DidExec () m_image_tokens.clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_instrumentation_runtimes.clear(); m_thread_list.DiscardThreadPlans(); m_memory_cache.Clear(true); + m_stop_info_override_callback = NULL; DoDidExec(); CompleteAttach (); // Flush the process (threads and all stack frames) after running CompleteAttach() @@ -6005,11 +6325,51 @@ Process::ResolveIndirectFunction(const Address *address, Error &error) void Process::ModulesDidLoad (ModuleList &module_list) { - SystemRuntime *sys_runtime = GetSystemRuntime(); - if (sys_runtime) - { - sys_runtime->ModulesDidLoad (module_list); - } + SystemRuntime *sys_runtime = GetSystemRuntime(); + if (sys_runtime) + { + sys_runtime->ModulesDidLoad (module_list); + } + + GetJITLoaders().ModulesDidLoad (module_list); + + // Give runtimes a chance to be created. + InstrumentationRuntime::ModulesDidLoad(module_list, this, m_instrumentation_runtimes); + + // Tell runtimes about new modules. + for (auto pos = m_instrumentation_runtimes.begin(); pos != m_instrumentation_runtimes.end(); ++pos) + { + InstrumentationRuntimeSP runtime = pos->second; + runtime->ModulesDidLoad(module_list); + } + +} + +ThreadCollectionSP +Process::GetHistoryThreads(lldb::addr_t addr) +{ + ThreadCollectionSP threads; + + const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(shared_from_this()); + + if (! memory_history.get()) { + return threads; + } + + threads.reset(new ThreadCollection(memory_history->GetHistoryThreads(addr))); + + return threads; +} - GetJITLoaders().ModulesDidLoad (module_list); +InstrumentationRuntimeSP +Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) +{ + InstrumentationRuntimeCollection::iterator pos; + pos = m_instrumentation_runtimes.find (type); + if (pos == m_instrumentation_runtimes.end()) + { + return InstrumentationRuntimeSP(); + } + else + return (*pos).second; } diff --git a/source/Target/ProcessLaunchInfo.cpp b/source/Target/ProcessLaunchInfo.cpp index 830f1470ed98f..451c42d18bfa3 100644 --- a/source/Target/ProcessLaunchInfo.cpp +++ b/source/Target/ProcessLaunchInfo.cpp @@ -9,10 +9,8 @@ #include "lldb/Host/Config.h" -#ifndef LLDB_DISABLE_POSIX -#include <spawn.h> -#endif - +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/Target.h" @@ -28,32 +26,32 @@ ProcessLaunchInfo::ProcessLaunchInfo () : ProcessInfo(), m_working_dir (), m_plugin_name (), - m_shell (), m_flags (0), m_file_actions (), - m_pty (), + m_pty (new lldb_utility::PseudoTerminal), m_resume_count (0), m_monitor_callback (NULL), m_monitor_callback_baton (NULL), m_monitor_signals (false), + m_listener_sp (), m_hijack_listener_sp () { } ProcessLaunchInfo::ProcessLaunchInfo(const char *stdin_path, const char *stdout_path, const char *stderr_path, - const char *working_directory, uint32_t launch_flags) - : ProcessInfo() - , m_working_dir() - , m_plugin_name() - , m_shell() - , m_flags(launch_flags) - , m_file_actions() - , m_pty() - , m_resume_count(0) - , m_monitor_callback(NULL) - , m_monitor_callback_baton(NULL) - , m_monitor_signals(false) - , m_hijack_listener_sp() + const char *working_directory, uint32_t launch_flags) : + ProcessInfo(), + m_working_dir(), + m_plugin_name(), + m_flags(launch_flags), + m_file_actions(), + m_pty(new lldb_utility::PseudoTerminal), + m_resume_count(0), + m_monitor_callback(NULL), + m_monitor_callback_baton(NULL), + m_monitor_signals(false), + m_listener_sp (), + m_hijack_listener_sp() { if (stdin_path) { @@ -184,27 +182,23 @@ ProcessLaunchInfo::SetProcessPluginName (const char *plugin) m_plugin_name.clear(); } -const char * +const FileSpec & ProcessLaunchInfo::GetShell () const { - if (m_shell.empty()) - return NULL; - return m_shell.c_str(); + return m_shell; } void -ProcessLaunchInfo::SetShell (const char * path) +ProcessLaunchInfo::SetShell (const FileSpec &shell) { - if (path && path[0]) + m_shell = shell; + if (m_shell) { - m_shell.assign (path); + m_shell.ResolveExecutableLocation(); m_flags.Set (lldb::eLaunchFlagLaunchInShell); } else - { - m_shell.clear(); m_flags.Clear (lldb::eLaunchFlagLaunchInShell); - } } void @@ -223,10 +217,11 @@ ProcessLaunchInfo::Clear () ProcessInfo::Clear(); m_working_dir.clear(); m_plugin_name.clear(); - m_shell.clear(); + m_shell.Clear(); m_flags.Clear(); m_file_actions.clear(); m_resume_count = 0; + m_listener_sp.reset(); m_hijack_listener_sp.reset(); } @@ -266,13 +261,23 @@ ProcessLaunchInfo::SetDetachOnError (bool enable) void ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty) { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + // If nothing for stdin or stdout or stderr was specified, then check the process for any default // settings that were set with "settings set" - if (GetFileActionForFD(STDIN_FILENO) == NULL || GetFileActionForFD(STDOUT_FILENO) == NULL || + if (GetFileActionForFD(STDIN_FILENO) == NULL || + GetFileActionForFD(STDOUT_FILENO) == NULL || GetFileActionForFD(STDERR_FILENO) == NULL) { + if (log) + log->Printf ("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr was not set, evaluating default handling", + __FUNCTION__); + if (m_flags.Test(eLaunchFlagDisableSTDIO)) { + if (log) + log->Printf ("ProcessLaunchInfo::%s eLaunchFlagDisableSTDIO set, adding suppression action for stdin, stdout and stderr", + __FUNCTION__); AppendSuppressFileAction (STDIN_FILENO , true, false); AppendSuppressFileAction (STDOUT_FILENO, false, true); AppendSuppressFileAction (STDERR_FILENO, false, true); @@ -288,34 +293,79 @@ ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty) FileSpec err_path; if (target) { - in_path = target->GetStandardInputPath(); - out_path = target->GetStandardOutputPath(); - err_path = target->GetStandardErrorPath(); + // Only override with the target settings if we don't already have + // an action for in, out or error + if (GetFileActionForFD(STDIN_FILENO) == NULL) + in_path = target->GetStandardInputPath(); + if (GetFileActionForFD(STDOUT_FILENO) == NULL) + out_path = target->GetStandardOutputPath(); + if (GetFileActionForFD(STDERR_FILENO) == NULL) + err_path = target->GetStandardErrorPath(); } + if (log) + log->Printf ("ProcessLaunchInfo::%s target stdin='%s', target stdout='%s', stderr='%s'", + __FUNCTION__, + in_path ? in_path.GetPath().c_str () : "<null>", + out_path ? out_path.GetPath().c_str () : "<null>", + err_path ? err_path.GetPath().c_str () : "<null>"); + char path[PATH_MAX]; if (in_path && in_path.GetPath(path, sizeof(path))) + { AppendOpenFileAction(STDIN_FILENO, path, true, false); + if (log) + log->Printf ("ProcessLaunchInfo::%s appended stdin open file action for %s", + __FUNCTION__, + in_path.GetPath().c_str ()); + } if (out_path && out_path.GetPath(path, sizeof(path))) + { AppendOpenFileAction(STDOUT_FILENO, path, false, true); + if (log) + log->Printf ("ProcessLaunchInfo::%s appended stdout open file action for %s", + __FUNCTION__, + out_path.GetPath().c_str ()); + } if (err_path && err_path.GetPath(path, sizeof(path))) + { + if (log) + log->Printf ("ProcessLaunchInfo::%s appended stderr open file action for %s", + __FUNCTION__, + err_path.GetPath().c_str ()); AppendOpenFileAction(STDERR_FILENO, path, false, true); + } + + if (default_to_use_pty && (!in_path || !out_path || !err_path)) + { + if (log) + log->Printf ("ProcessLaunchInfo::%s default_to_use_pty is set, and at least one stdin/stderr/stdout is unset, so generating a pty to use for it", + __FUNCTION__); - if (default_to_use_pty && (!in_path || !out_path || !err_path)) { - if (m_pty.OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) { - const char *slave_path = m_pty.GetSlaveName(NULL, 0); + if (m_pty->OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) + { + const char *slave_path = m_pty->GetSlaveName(NULL, 0); - if (!in_path) { + // Only use the slave tty if we don't have anything specified for + // input and don't have an action for stdin + if (!in_path && GetFileActionForFD(STDIN_FILENO) == NULL) + { AppendOpenFileAction(STDIN_FILENO, slave_path, true, false); } - if (!out_path) { + // Only use the slave tty if we don't have anything specified for + // output and don't have an action for stdout + if (!out_path && GetFileActionForFD(STDOUT_FILENO) == NULL) + { AppendOpenFileAction(STDOUT_FILENO, slave_path, false, true); } - if (!err_path) { + // Only use the slave tty if we don't have anything specified for + // error and don't have an action for stderr + if (!err_path && GetFileActionForFD(STDERR_FILENO) == NULL) + { AppendOpenFileAction(STDERR_FILENO, slave_path, false, true); } } @@ -336,42 +386,30 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, if (GetFlags().Test (eLaunchFlagLaunchInShell)) { - const char *shell_executable = GetShell(); - if (shell_executable) + if (m_shell) { - char shell_resolved_path[PATH_MAX]; - - if (localhost) - { - FileSpec shell_filespec (shell_executable, true); - - if (!shell_filespec.Exists()) - { - // Resolve the path in case we just got "bash", "sh" or "tcsh" - if (!shell_filespec.ResolveExecutableLocation ()) - { - error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable); - return false; - } - } - shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path)); - shell_executable = shell_resolved_path; - } + std::string shell_executable = m_shell.GetPath(); const char **argv = GetArguments().GetConstArgumentVector (); if (argv == NULL || argv[0] == NULL) return false; Args shell_arguments; std::string safe_arg; - shell_arguments.AppendArgument (shell_executable); - shell_arguments.AppendArgument ("-c"); + shell_arguments.AppendArgument (shell_executable.c_str()); + const llvm::Triple &triple = GetArchitecture().GetTriple(); + if (triple.getOS() == llvm::Triple::Win32 && !triple.isWindowsCygwinEnvironment()) + shell_arguments.AppendArgument("/C"); + else + shell_arguments.AppendArgument("-c"); + StreamString shell_command; if (will_debug) { // Add a modified PATH environment variable in case argv[0] - // is a relative path + // is a relative path. const char *argv0 = argv[0]; - if (argv0 && (argv0[0] != '/' && argv0[0] != '~')) + FileSpec arg_spec(argv0, false); + if (arg_spec.IsRelativeToCurrentWorkingDirectory()) { // We have a relative path to our executable which may not work if // we just try to run "a.out" (without it being converted to "./a.out") @@ -402,7 +440,8 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, shell_command.PutCString(new_path.c_str()); } - shell_command.PutCString ("exec"); + if (triple.getOS() != llvm::Triple::Win32 || triple.isWindowsCygwinEnvironment()) + shell_command.PutCString("exec"); // Only Apple supports /usr/bin/arch being able to specify the architecture if (GetArchitecture().IsValid() && // Valid architecture @@ -442,7 +481,7 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, } } shell_arguments.AppendArgument (shell_command.GetString().c_str()); - m_executable.SetFile(shell_executable, false); + m_executable = m_shell; m_arguments = shell_arguments; return true; } @@ -457,3 +496,12 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, } return false; } + +Listener & +ProcessLaunchInfo::GetListenerForProcess (Debugger &debugger) +{ + if (m_listener_sp) + return *m_listener_sp; + else + return debugger.GetListener(); +} diff --git a/source/Target/StackFrame.cpp b/source/Target/StackFrame.cpp index e497b176ccfe2..c46db4cfadf9b 100644 --- a/source/Target/StackFrame.cpp +++ b/source/Target/StackFrame.cpp @@ -70,7 +70,8 @@ StackFrame::StackFrame (const ThreadSP &thread_sp, m_is_history_frame (is_history_frame), m_variable_list_sp (), m_variable_list_value_objects (), - m_disassembly () + m_disassembly (), + m_mutex (Mutex::eMutexTypeRecursive) { // If we don't have a CFA value, use the frame index for our StackID so that recursive // functions properly aren't confused with one another on a history stack. @@ -109,7 +110,8 @@ StackFrame::StackFrame (const ThreadSP &thread_sp, m_is_history_frame (false), m_variable_list_sp (), m_variable_list_value_objects (), - m_disassembly () + m_disassembly (), + m_mutex (Mutex::eMutexTypeRecursive) { if (sc_ptr != NULL) { @@ -148,7 +150,8 @@ StackFrame::StackFrame (const ThreadSP &thread_sp, m_is_history_frame (false), m_variable_list_sp (), m_variable_list_value_objects (), - m_disassembly () + m_disassembly (), + m_mutex (Mutex::eMutexTypeRecursive) { if (sc_ptr != NULL) { @@ -189,6 +192,7 @@ StackFrame::~StackFrame() StackID& StackFrame::GetStackID() { + Mutex::Locker locker(m_mutex); // Make sure we have resolved the StackID object's symbol context scope if // we already haven't looked it up. @@ -235,6 +239,7 @@ StackFrame::GetFrameIndex () const void StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope) { + Mutex::Locker locker(m_mutex); m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE); m_id.SetSymbolContextScope (symbol_scope); } @@ -242,6 +247,7 @@ StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope) const Address& StackFrame::GetFrameCodeAddress() { + Mutex::Locker locker(m_mutex); if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset()) { m_flags.Set (RESOLVED_FRAME_CODE_ADDR); @@ -272,6 +278,7 @@ StackFrame::GetFrameCodeAddress() bool StackFrame::ChangePC (addr_t pc) { + Mutex::Locker locker(m_mutex); // We can't change the pc value of a history stack frame - it is immutable. if (m_is_history_frame) return false; @@ -287,6 +294,7 @@ StackFrame::ChangePC (addr_t pc) const char * StackFrame::Disassemble () { + Mutex::Locker locker(m_mutex); if (m_disassembly.GetSize() == 0) { ExecutionContext exe_ctx (shared_from_this()); @@ -346,6 +354,7 @@ StackFrame::GetFrameBlock () const SymbolContext& StackFrame::GetSymbolContext (uint32_t resolve_scope) { + Mutex::Locker locker(m_mutex); // Copy our internal symbol context into "sc". if ((m_flags.Get() & resolve_scope) != resolve_scope) { @@ -375,7 +384,31 @@ StackFrame::GetSymbolContext (uint32_t resolve_scope) { addr_t offset = lookup_addr.GetOffset(); if (offset > 0) + { lookup_addr.SetOffset(offset - 1); + + } + else + { + // lookup_addr is the start of a section. We need + // do the math on the actual load address and re-compute + // the section. We're working with a 'noreturn' function + // at the end of a section. + ThreadSP thread_sp (GetThread()); + if (thread_sp) + { + TargetSP target_sp (thread_sp->CalculateTarget()); + if (target_sp) + { + addr_t addr_minus_one = lookup_addr.GetLoadAddress(target_sp.get()) - 1; + lookup_addr.SetLoadAddress (addr_minus_one, target_sp.get()); + } + else + { + lookup_addr.SetOffset(offset - 1); + } + } + } } @@ -504,6 +537,7 @@ StackFrame::GetSymbolContext (uint32_t resolve_scope) VariableList * StackFrame::GetVariableList (bool get_file_globals) { + Mutex::Locker locker(m_mutex); if (m_flags.IsClear(RESOLVED_VARIABLES)) { m_flags.Set(RESOLVED_VARIABLES); @@ -544,6 +578,7 @@ StackFrame::GetVariableList (bool get_file_globals) VariableListSP StackFrame::GetInScopeVariableList (bool get_file_globals) { + Mutex::Locker locker(m_mutex); // We can't fetch variable information for a history stack frame. if (m_is_history_frame) return VariableListSP(); @@ -680,8 +715,8 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr, // Make sure we aren't trying to deref an objective // C ivar if this is not allowed const uint32_t pointer_type_flags = valobj_sp->GetClangType().GetTypeInfo (NULL); - if ((pointer_type_flags & ClangASTType::eTypeIsObjC) && - (pointer_type_flags & ClangASTType::eTypeIsPointer)) + if ((pointer_type_flags & eTypeIsObjC) && + (pointer_type_flags & eTypeIsPointer)) { // This was an objective C object pointer and // it was requested we skip any fragile ivars @@ -1142,6 +1177,7 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr, bool StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr) { + Mutex::Locker locker(m_mutex); if (m_cfa_is_valid == false) { m_frame_base_error.SetErrorString("No frame base available for this historical stack frame."); @@ -1191,6 +1227,7 @@ StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr) RegisterContextSP StackFrame::GetRegisterContext () { + Mutex::Locker locker(m_mutex); if (!m_reg_context_sp) { ThreadSP thread_sp (GetThread()); @@ -1211,6 +1248,7 @@ StackFrame::HasDebugInformation () ValueObjectSP StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic) { + Mutex::Locker locker(m_mutex); ValueObjectSP valobj_sp; if (m_is_history_frame) { @@ -1246,6 +1284,7 @@ StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp, Dynam ValueObjectSP StackFrame::TrackGlobalVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic) { + Mutex::Locker locker(m_mutex); if (m_is_history_frame) return ValueObjectSP(); @@ -1365,17 +1404,20 @@ StackFrame::Dump (Stream *strm, bool show_frame_index, bool show_fullpaths) GetSymbolContext(eSymbolContextEverything); const bool show_module = true; const bool show_inline = true; + const bool show_function_arguments = true; m_sc.DumpStopContext (strm, exe_ctx.GetBestExecutionContextScope(), GetFrameCodeAddress(), show_fullpaths, show_module, - show_inline); + show_inline, + show_function_arguments); } void StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame) { + Mutex::Locker locker(m_mutex); assert (GetStackID() == prev_frame.GetStackID()); // TODO: remove this after some testing m_variable_list_sp = prev_frame.m_variable_list_sp; m_variable_list_value_objects.Swap (prev_frame.m_variable_list_value_objects); @@ -1387,6 +1429,7 @@ StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame) void StackFrame::UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame) { + Mutex::Locker locker(m_mutex); assert (GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing m_id.SetPC (curr_frame.m_id.GetPC()); // Update the Stack ID PC value assert (GetThread() == curr_frame.GetThread()); diff --git a/source/Target/StackFrameList.cpp b/source/Target/StackFrameList.cpp index 99234dc61f1d9..9a132618288dc 100644 --- a/source/Target/StackFrameList.cpp +++ b/source/Target/StackFrameList.cpp @@ -288,8 +288,8 @@ StackFrameList::GetFramesUpTo(uint32_t end_idx) do { uint32_t idx = m_concrete_frames_fetched++; - lldb::addr_t pc; - lldb::addr_t cfa; + lldb::addr_t pc = LLDB_INVALID_ADDRESS; + lldb::addr_t cfa = LLDB_INVALID_ADDRESS; if (idx == 0) { // We might have already created frame zero, only create it @@ -625,11 +625,14 @@ StackFrameList::GetFrameWithStackID (const StackID &stack_id) if (begin != end) { collection::const_iterator pos = std::lower_bound (begin, end, stack_id, CompareStackID); - if (pos != end && (*pos)->GetStackID() == stack_id) - return *pos; + if (pos != end) + { + if ((*pos)->GetStackID() == stack_id) + return *pos; + } - if (m_frames.back()->GetStackID() < stack_id) - frame_idx = m_frames.size(); +// if (m_frames.back()->GetStackID() < stack_id) +// frame_idx = m_frames.size(); } do { diff --git a/source/Target/StopInfo.cpp b/source/Target/StopInfo.cpp index a37a4079ff116..4b57ca65a2dff 100644 --- a/source/Target/StopInfo.cpp +++ b/source/Target/StopInfo.cpp @@ -40,7 +40,8 @@ StopInfo::StopInfo (Thread &thread, uint64_t value) : m_resume_id (thread.GetProcess()->GetResumeID()), m_value (value), m_override_should_notify (eLazyBoolCalculate), - m_override_should_stop (eLazyBoolCalculate) + m_override_should_stop (eLazyBoolCalculate), + m_extended_info() { } @@ -181,6 +182,7 @@ public: { ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context (event_ptr, exe_ctx, true); + bp_site_sp->BumpHitCounts(); m_should_stop = bp_site_sp->ShouldStop (&context); } else @@ -402,10 +404,21 @@ protected: // Let's copy the breakpoint locations out of the site and store them in a local list. That way if // one of the breakpoint actions changes the site, then we won't be operating on a bad list. + // For safety's sake let's also grab an extra reference to the breakpoint owners of the locations we're + // going to examine, since the locations are going to have to get back to their breakpoints, and the + // locations don't keep their owners alive. I'm just sticking the BreakpointSP's in a vector since + // I'm only really using it to locally increment their retain counts. BreakpointLocationCollection site_locations; + std::vector<lldb::BreakpointSP> location_owners; + for (size_t j = 0; j < num_owners; j++) - site_locations.Add(bp_site_sp->GetOwnerAtIndex(j)); + { + BreakpointLocationSP loc(bp_site_sp->GetOwnerAtIndex(j)); + site_locations.Add(loc); + location_owners.push_back(loc->GetBreakpoint().shared_from_this()); + + } for (size_t j = 0; j < num_owners; j++) { @@ -689,14 +702,13 @@ protected: assert (stored_stop_info_sp.get() == this); ThreadPlanSP new_plan_sp(thread_sp->QueueThreadPlanForStepSingleInstruction(false, // step-over - false, // abort_other_plans - true)); // stop_other_threads + false, // abort_other_plans + true)); // stop_other_threads new_plan_sp->SetIsMasterPlan (true); new_plan_sp->SetOkayToDiscard (false); new_plan_sp->SetPrivate (true); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); - process->Resume (); - process->WaitForProcessToStop (NULL); + process->ResumeSynchronous(NULL); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); thread_sp->SetStopInfo(stored_stop_info_sp); } diff --git a/source/Target/Target.cpp b/source/Target/Target.cpp index d2d0b50985550..e9393d1be0b41 100644 --- a/source/Target/Target.cpp +++ b/source/Target/Target.cpp @@ -35,6 +35,7 @@ #include "lldb/Core/ValueObject.h" #include "lldb/Expression/ClangASTSource.h" #include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -63,7 +64,7 @@ Target::GetStaticBroadcasterClass () //---------------------------------------------------------------------- // Target constructor //---------------------------------------------------------------------- -Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp) : +Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp, bool is_dummy_target) : TargetProperties (this), Broadcaster (&debugger, Target::GetStaticBroadcasterClass().AsCString()), ExecutionContextScope (), @@ -87,7 +88,9 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::Plat m_stop_hooks (), m_stop_hook_next_id (0), m_valid (true), - m_suppress_stop_hooks (false) + m_suppress_stop_hooks (false), + m_is_dummy_target(is_dummy_target) + { SetEventName (eBroadcastBitBreakpointChanged, "breakpoint-changed"); SetEventName (eBroadcastBitModulesLoaded, "modules-loaded"); @@ -106,6 +109,24 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::Plat } } +void +Target::PrimeFromDummyTarget(Target *target) +{ + if (!target) + return; + + m_stop_hooks = target->m_stop_hooks; + + for (BreakpointSP breakpoint_sp : target->m_breakpoint_list.Breakpoints()) + { + if (breakpoint_sp->IsInternal()) + continue; + + BreakpointSP new_bp (new Breakpoint (*this, *breakpoint_sp.get())); + AddBreakpoint (new_bp, false); + } +} + //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- @@ -330,7 +351,7 @@ Target::CreateBreakpoint (lldb::addr_t addr, bool internal, bool hardware) BreakpointSP Target::CreateBreakpoint (Address &addr, bool internal, bool hardware) { - SearchFilterSP filter_sp(new SearchFilterForNonModuleSpecificSearches (shared_from_this())); + SearchFilterSP filter_sp(new SearchFilterForUnconstrainedSearches (shared_from_this())); BreakpointResolverSP resolver_sp (new BreakpointResolverAddress (NULL, addr)); return CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, false); } @@ -430,7 +451,7 @@ Target::GetSearchFilterForModule (const FileSpec *containingModule) else { if (m_search_filter_sp.get() == NULL) - m_search_filter_sp.reset (new SearchFilterForNonModuleSpecificSearches (shared_from_this())); + m_search_filter_sp.reset (new SearchFilterForUnconstrainedSearches (shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; @@ -449,7 +470,7 @@ Target::GetSearchFilterForModuleList (const FileSpecList *containingModules) else { if (m_search_filter_sp.get() == NULL) - m_search_filter_sp.reset (new SearchFilterForNonModuleSpecificSearches (shared_from_this())); + m_search_filter_sp.reset (new SearchFilterForUnconstrainedSearches (shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; @@ -510,29 +531,35 @@ Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resol { bp_sp.reset(new Breakpoint (*this, filter_sp, resolver_sp, request_hardware, resolve_indirect_symbols)); resolver_sp->SetBreakpoint (bp_sp.get()); + AddBreakpoint (bp_sp, internal); + } + return bp_sp; +} - if (internal) - m_internal_breakpoint_list.Add (bp_sp, false); - else - m_breakpoint_list.Add (bp_sp, true); - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); - if (log) - { - StreamString s; - bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); - log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, internal ? "yes" : "no", s.GetData()); - } +void +Target::AddBreakpoint (lldb::BreakpointSP bp_sp, bool internal) +{ + if (!bp_sp) + return; + if (internal) + m_internal_breakpoint_list.Add (bp_sp, false); + else + m_breakpoint_list.Add (bp_sp, true); - bp_sp->ResolveBreakpoint(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + { + StreamString s; + bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, bp_sp->IsInternal() ? "yes" : "no", s.GetData()); } - - if (!internal && bp_sp) + + bp_sp->ResolveBreakpoint(); + + if (!internal) { m_last_created_breakpoint = bp_sp; } - - return bp_sp; } bool @@ -1214,6 +1241,7 @@ Target::ModulesDidUnload (ModuleList &module_list, bool delete_locations) { if (m_valid && module_list.GetSize()) { + UnloadModuleSections (module_list); m_breakpoint_list.UpdateBreakpoints (module_list, false, delete_locations); // TODO: make event data that packages up the module_list BroadcastEvent (eBroadcastBitModulesUnloaded, NULL); @@ -1221,7 +1249,7 @@ Target::ModulesDidUnload (ModuleList &module_list, bool delete_locations) } bool -Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_file_spec) +Target::ModuleIsExcludedForUnconstrainedSearches (const FileSpec &module_file_spec) { if (GetBreakpointsConsultPlatformAvoidList()) { @@ -1235,7 +1263,7 @@ Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_fil { for (size_t i = 0; i < num_modules; i++) { - if (!ModuleIsExcludedForNonModuleSpecificSearches (matchingModules.GetModuleAtIndex(i))) + if (!ModuleIsExcludedForUnconstrainedSearches (matchingModules.GetModuleAtIndex(i))) return false; } return true; @@ -1245,12 +1273,12 @@ Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_fil } bool -Target::ModuleIsExcludedForNonModuleSpecificSearches (const lldb::ModuleSP &module_sp) +Target::ModuleIsExcludedForUnconstrainedSearches (const lldb::ModuleSP &module_sp) { if (GetBreakpointsConsultPlatformAvoidList()) { if (m_platform_sp) - return m_platform_sp->ModuleIsExcludedForNonModuleSpecificSearches (*this, module_sp); + return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches (*this, module_sp); } return false; } @@ -1355,9 +1383,9 @@ Target::ReadMemory (const Address& addr, ModuleSP addr_module_sp (resolved_addr.GetModule()); if (addr_module_sp && addr_module_sp->GetFileSpec()) error.SetErrorStringWithFormat("%s[0x%" PRIx64 "] can't be resolved, %s in not currently loaded", - addr_module_sp->GetFileSpec().GetFilename().AsCString(), + addr_module_sp->GetFileSpec().GetFilename().AsCString("<Unknown>"), resolved_addr.GetFileAddress(), - addr_module_sp->GetFileSpec().GetFilename().AsCString()); + addr_module_sp->GetFileSpec().GetFilename().AsCString("<Unknonw>")); else error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", resolved_addr.GetFileAddress()); } @@ -1436,7 +1464,12 @@ Target::ReadCStringFromMemory (const Address& addr, char *dst, size_t dst_max_le Error error; addr_t curr_addr = addr.GetLoadAddress(this); Address address(addr); + + // We could call m_process_sp->GetMemoryCacheLineSize() but I don't + // think this really needs to be tied to the memory cache subsystem's + // cache line size, so leave this as a fixed constant. const size_t cache_line_size = 512; + size_t bytes_left = dst_max_len - 1; char *curr_dst = dst; @@ -2006,6 +2039,22 @@ Target::GetSourceManager () return *m_source_manager_ap; } +ClangModulesDeclVendor * +Target::GetClangModulesDeclVendor () +{ + static Mutex s_clang_modules_decl_vendor_mutex; // If this is contended we can make it per-target + + { + Mutex::Locker clang_modules_decl_vendor_locker(s_clang_modules_decl_vendor_mutex); + + if (!m_clang_modules_decl_vendor_ap) + { + m_clang_modules_decl_vendor_ap.reset(ClangModulesDeclVendor::Create(*this)); + } + } + + return m_clang_modules_decl_vendor_ap.get(); +} Target::StopHookSP Target::CreateStopHook () @@ -2173,18 +2222,17 @@ Target::RunStopHooks () if (print_thread_header) result.AppendMessageWithFormat("-- Thread %d\n", exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID()); - - bool stop_on_continue = true; - bool stop_on_error = true; - bool echo_commands = false; - bool print_results = true; - GetDebugger().GetCommandInterpreter().HandleCommands (cur_hook_sp->GetCommands(), - &exc_ctx_with_reasons[i], - stop_on_continue, - stop_on_error, - echo_commands, - print_results, - eLazyBoolNo, + + CommandInterpreterRunOptions options; + options.SetStopOnContinue (true); + options.SetStopOnError (true); + options.SetEchoCommands (false); + options.SetPrintResults (true); + options.SetAddToHistory (false); + + GetDebugger().GetCommandInterpreter().HandleCommands (cur_hook_sp->GetCommands(), + &exc_ctx_with_reasons[i], + options, result); // If the command started the target going again, we should bag out of @@ -2279,6 +2327,12 @@ Target::ResolveLoadAddress (addr_t load_addr, Address &so_addr, uint32_t stop_id } bool +Target::ResolveFileAddress (lldb::addr_t file_addr, Address &resolved_addr) +{ + return m_images.ResolveFileAddress(file_addr, resolved_addr); +} + +bool Target::SetSectionLoadAddress (const SectionSP §ion_sp, addr_t new_section_load_addr, bool warn_multiple) { const addr_t old_section_load_addr = m_section_load_history.GetSectionLoadAddress (SectionLoadHistory::eStopIDNow, section_sp); @@ -2297,6 +2351,40 @@ Target::SetSectionLoadAddress (const SectionSP §ion_sp, addr_t new_section_l } +size_t +Target::UnloadModuleSections (const ModuleList &module_list) +{ + size_t section_unload_count = 0; + size_t num_modules = module_list.GetSize(); + for (size_t i=0; i<num_modules; ++i) + { + section_unload_count += UnloadModuleSections (module_list.GetModuleAtIndex(i)); + } + return section_unload_count; +} + +size_t +Target::UnloadModuleSections (const lldb::ModuleSP &module_sp) +{ + uint32_t stop_id = 0; + ProcessSP process_sp(GetProcessSP()); + if (process_sp) + stop_id = process_sp->GetStopID(); + else + stop_id = m_section_load_history.GetLastStopID(); + SectionList *sections = module_sp->GetSectionList(); + size_t section_unload_count = 0; + if (sections) + { + const uint32_t num_sections = sections->GetNumSections(0); + for (uint32_t i = 0; i < num_sections; ++i) + { + section_unload_count += m_section_load_history.SetSectionUnloaded(stop_id, sections->GetSectionAtIndex(i)); + } + } + return section_unload_count; +} + bool Target::SetSectionUnloaded (const lldb::SectionSP §ion_sp) { @@ -2329,10 +2417,14 @@ Target::ClearAllLoadedSections () Error -Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) +Target::Launch (ProcessLaunchInfo &launch_info, Stream *stream) { Error error; - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET)); + + if (log) + log->Printf ("Target::%s() called for %s", __FUNCTION__, launch_info.GetExecutableFile().GetPath().c_str ()); + StateType state = eStateInvalid; // Scope to temporarily get the process state in case someone has manually @@ -2342,7 +2434,16 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) ProcessSP process_sp (GetProcessSP()); if (process_sp) + { state = process_sp->GetState(); + if (log) + log->Printf ("Target::%s the process exists, and its current state is %s", __FUNCTION__, StateAsCString (state)); + } + else + { + if (log) + log->Printf ("Target::%s the process instance doesn't currently exist.", __FUNCTION__); + } } launch_info.GetFlags().Set (eLaunchFlagDebug); @@ -2358,6 +2459,13 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) // 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; + if (log) + log->Printf ("Target::%s have platform=%s, platform_sp->IsHost()=%s, default_to_use_pty=%s", + __FUNCTION__, + platform_sp ? "true" : "false", + platform_sp ? (platform_sp->IsHost () ? "true" : "false") : "n/a", + default_to_use_pty ? "true" : "false"); + launch_info.FinalizeFileActions (this, default_to_use_pty); if (state == eStateConnected) @@ -2375,14 +2483,19 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) // If we're not already connected to the process, and if we have a platform that can launch a process for debugging, go ahead and do that here. if (state != eStateConnected && platform_sp && platform_sp->CanDebugProcess ()) { + if (log) + log->Printf ("Target::%s asking the platform to debug the process", __FUNCTION__); + m_process_sp = GetPlatform()->DebugProcess (launch_info, debugger, this, - listener, error); } else { + if (log) + log->Printf ("Target::%s the platform doesn't know how to debug a process, getting a process plugin to do this for us.", __FUNCTION__); + if (state == eStateConnected) { assert(m_process_sp); @@ -2391,7 +2504,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) { // Use a Process plugin to construct the process. const char *plugin_name = launch_info.GetProcessPluginName(); - CreateProcess (listener, plugin_name, NULL); + CreateProcess (launch_info.GetListenerForProcess(debugger), plugin_name, NULL); } // Since we didn't have a platform launch the process, launch it here. @@ -2411,8 +2524,8 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == false) { ListenerSP hijack_listener_sp (launch_info.GetHijackListener()); - - StateType state = m_process_sp->WaitForProcessToStop (NULL, NULL, false, hijack_listener_sp.get()); + + StateType state = m_process_sp->WaitForProcessToStop (NULL, NULL, false, hijack_listener_sp.get(), NULL); if (state == eStateStopped) { @@ -2430,7 +2543,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) if (synchronous_execution) { - state = m_process_sp->WaitForProcessToStop (NULL, NULL, true, hijack_listener_sp.get()); + state = m_process_sp->WaitForProcessToStop (NULL, NULL, true, hijack_listener_sp.get(), stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) { @@ -2447,7 +2560,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) } else if (state == eStateExited) { - bool with_shell = launch_info.GetShell(); + bool with_shell = !!launch_info.GetShell(); const int exit_status = m_process_sp->GetExitStatus(); const char *exit_desc = m_process_sp->GetExitDescription(); #define LAUNCH_SHELL_MESSAGE "\n'r' and 'run' are aliases that default to launching through a shell.\nTry launching without going through a shell by using 'process launch'." @@ -2632,7 +2745,7 @@ g_properties[] = { { "default-arch" , OptionValue::eTypeArch , true , 0 , NULL, NULL, "Default architecture to choose, when there's a choice." }, { "expr-prefix" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "Path to a file containing expressions to be prepended to all expressions." }, - { "prefer-dynamic-value" , OptionValue::eTypeEnum , false, eNoDynamicValues , NULL, g_dynamic_value_types, "Should printed values be shown as their dynamic value." }, + { "prefer-dynamic-value" , OptionValue::eTypeEnum , false, eDynamicDontRunTarget , NULL, g_dynamic_value_types, "Should printed values be shown as their dynamic value." }, { "enable-synthetic-value" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Should synthetic values be used by default whenever available." }, { "skip-prologue" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Skip function prologues when setting breakpoints by name." }, { "source-map" , OptionValue::eTypePathMap , false, 0 , NULL, NULL, "Source path remappings used to track the change of location between a source file when built, and " @@ -2656,12 +2769,13 @@ g_properties[] = { "detach-on-error" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "debugserver will detach (rather than killing) a process if it loses connection with lldb." }, { "disable-aslr" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Disable Address Space Layout Randomization (ASLR)" }, { "disable-stdio" , OptionValue::eTypeBoolean , false, false , NULL, NULL, "Disable stdin/stdout for process (e.g. for a GUI application)" }, - { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsHeaders , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " + { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsAlways , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " "Breakpoint locations can end up being inlined by the compiler, so that a compile unit 'a.c' might contain an inlined function from another source file. " "Usually this is limitted to breakpoint locations from inlined functions from header or other include files, or more accurately non-implementation source files. " "Sometimes code might #include implementation files and cause inlined breakpoint locations in inlined implementation files. " - "Always checking for inlined breakpoint locations can be expensive (memory and time), so we try to minimize the " - "times we look for inlined locations. This setting allows you to control exactly which strategy is used when settings " + "Always checking for inlined breakpoint locations can be expensive (memory and time), so if you have a project with many headers " + "and find that setting breakpoints is slow, then you can change this setting to headers. " + "This setting allows you to control exactly which strategy is used when setting " "file and line breakpoints." }, // FIXME: This is the wrong way to do per-architecture settings, but we don't have a general per architecture settings system in place yet. { "x86-disassembly-flavor" , OptionValue::eTypeEnum , false, eX86DisFlavorDefault, NULL, g_x86_dis_flavor_value_types, "The default disassembly flavor to use for x86 or x86-64 targets." }, diff --git a/source/Target/TargetList.cpp b/source/Target/TargetList.cpp index 5ee75ff744493..28ad47e3d81d7 100644 --- a/source/Target/TargetList.cpp +++ b/source/Target/TargetList.cpp @@ -21,6 +21,7 @@ #include "lldb/Core/State.h" #include "lldb/Core/Timer.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Symbol/ObjectFile.h" @@ -69,6 +70,41 @@ TargetList::CreateTarget (Debugger &debugger, const OptionGroupPlatform *platform_options, TargetSP &target_sp) { + return CreateTargetInternal (debugger, + user_exe_path, + triple_cstr, + get_dependent_files, + platform_options, + target_sp, + false); +} + +Error +TargetList::CreateTarget (Debugger &debugger, + const char *user_exe_path, + const ArchSpec& specified_arch, + bool get_dependent_files, + PlatformSP &platform_sp, + TargetSP &target_sp) +{ + return CreateTargetInternal (debugger, + user_exe_path, + specified_arch, + get_dependent_files, + platform_sp, + target_sp, + false); +} + +Error +TargetList::CreateTargetInternal (Debugger &debugger, + const char *user_exe_path, + const char *triple_cstr, + bool get_dependent_files, + const OptionGroupPlatform *platform_options, + TargetSP &target_sp, + bool is_dummy_target) +{ Error error; PlatformSP platform_sp; @@ -170,7 +206,7 @@ TargetList::CreateTarget (Debugger &debugger, typedef std::vector<PlatformSP> PlatformList; PlatformList platforms; - PlatformSP host_platform_sp = Platform::GetDefaultPlatform(); + PlatformSP host_platform_sp = Platform::GetHostPlatform(); for (size_t i=0; i<num_specs; ++i) { ModuleSpec module_spec; @@ -258,7 +294,11 @@ TargetList::CreateTarget (Debugger &debugger, if (!prefer_platform_arch && arch.IsValid()) { if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) + { platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch); + if (platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } } else if (platform_arch.IsValid()) { @@ -266,30 +306,69 @@ TargetList::CreateTarget (Debugger &debugger, // a single architecture which should be used ArchSpec fixed_platform_arch; if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, &fixed_platform_arch)) + { platform_sp = Platform::GetPlatformForArchitecture(platform_arch, &fixed_platform_arch); + if (platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } } } if (!platform_arch.IsValid()) platform_arch = arch; - error = TargetList::CreateTarget (debugger, - user_exe_path, - platform_arch, - get_dependent_files, - platform_sp, - target_sp); + error = TargetList::CreateTargetInternal (debugger, + user_exe_path, + platform_arch, + get_dependent_files, + platform_sp, + target_sp, + is_dummy_target); return error; } +lldb::TargetSP +TargetList::GetDummyTarget (lldb_private::Debugger &debugger) +{ + // FIXME: Maybe the dummy target should be per-Debugger + if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) + { + ArchSpec arch(Target::GetDefaultArchitecture()); + if (!arch.IsValid()) + arch = HostInfo::GetArchitecture(); + Error err = CreateDummyTarget(debugger, + arch.GetTriple().getTriple().c_str(), + m_dummy_target_sp); + } + + return m_dummy_target_sp; +} + Error -TargetList::CreateTarget (Debugger &debugger, - const char *user_exe_path, - const ArchSpec& specified_arch, - bool get_dependent_files, - PlatformSP &platform_sp, - TargetSP &target_sp) +TargetList::CreateDummyTarget (Debugger &debugger, + const char *specified_arch_name, + lldb::TargetSP &target_sp) { + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + return CreateTargetInternal (debugger, + (const char *) nullptr, + specified_arch_name, + false, + (const OptionGroupPlatform *) nullptr, + target_sp, + true); +} + +Error +TargetList::CreateTargetInternal (Debugger &debugger, + const char *user_exe_path, + const ArchSpec& specified_arch, + bool get_dependent_files, + lldb::PlatformSP &platform_sp, + lldb::TargetSP &target_sp, + bool is_dummy_target) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, "TargetList::CreateTarget (file = '%s', arch = '%s')", user_exe_path, @@ -332,7 +411,7 @@ TargetList::CreateTarget (Debugger &debugger, if (file.GetFileType() == FileSpec::eFileTypeDirectory) user_exe_path_is_bundle = true; - if (file.IsRelativeToCurrentWorkingDirectory()) + if (file.IsRelativeToCurrentWorkingDirectory() && user_exe_path) { // Ignore paths that start with "./" and "../" if (!((user_exe_path[0] == '.' && user_exe_path[1] == '/') || @@ -355,8 +434,8 @@ TargetList::CreateTarget (Debugger &debugger, if (platform_sp) { FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); - error = platform_sp->ResolveExecutable (file, - arch, + ModuleSpec module_spec(file, arch); + error = platform_sp->ResolveExecutable (module_spec, exe_module_sp, executable_search_paths.GetSize() ? &executable_search_paths : NULL); } @@ -378,7 +457,7 @@ TargetList::CreateTarget (Debugger &debugger, } return error; } - target_sp.reset(new Target(debugger, arch, platform_sp)); + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); if (user_exe_path_is_bundle) exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, sizeof(resolved_bundle_exe_path)); @@ -388,7 +467,7 @@ TargetList::CreateTarget (Debugger &debugger, { // No file was specified, just create an empty target with any arch // if a valid arch was specified - target_sp.reset(new Target(debugger, arch, platform_sp)); + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); } if (target_sp) @@ -415,11 +494,20 @@ TargetList::CreateTarget (Debugger &debugger, file_dir.GetDirectory() = file.GetDirectory(); target_sp->GetExecutableSearchPaths ().Append (file_dir); } - Mutex::Locker locker(m_target_list_mutex); - m_selected_target_idx = m_target_list.size(); - m_target_list.push_back(target_sp); - - + + // Don't put the dummy target in the target list, it's held separately. + if (!is_dummy_target) + { + Mutex::Locker locker(m_target_list_mutex); + m_selected_target_idx = m_target_list.size(); + m_target_list.push_back(target_sp); + // Now prime this from the dummy target: + target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget()); + } + else + { + m_dummy_target_sp = target_sp; + } } return error; diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp index a445517da6a8e..b532d8d71c8f8 100644 --- a/source/Target/Thread.cpp +++ b/source/Target/Thread.cpp @@ -32,6 +32,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/ThreadPlanPython.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepOverBreakpoint.h" @@ -43,7 +44,7 @@ #include "lldb/Target/ThreadSpec.h" #include "lldb/Target/Unwind.h" #include "Plugins/Process/Utility/UnwindLLDB.h" -#include "UnwindMacOSXFrameBackchain.h" +#include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" using namespace lldb; @@ -278,6 +279,7 @@ Thread::Thread (Process &process, lldb::tid_t tid, bool use_invalid_index_id) : m_process_wp (process.shared_from_this()), m_stop_info_sp (), m_stop_info_stop_id (0), + m_stop_info_override_stop_id (0), m_index_id (use_invalid_index_id ? LLDB_INVALID_INDEX32 : process.GetNextThreadIndexID(tid)), m_reg_context_sp (), m_state (eStateUnloaded), @@ -465,6 +467,24 @@ Thread::GetPrivateStopInfo () SetStopInfo (StopInfoSP()); } } + + // The stop info can be manually set by calling Thread::SetStopInfo() + // prior to this function ever getting called, so we can't rely on + // "m_stop_info_stop_id != process_stop_id" as the condition for + // the if statement below, we must also check the stop info to see + // if we need to override it. See the header documentation in + // Process::GetStopInfoOverrideCallback() for more information on + // the stop info override callback. + if (m_stop_info_override_stop_id != process_stop_id) + { + m_stop_info_override_stop_id = process_stop_id; + if (m_stop_info_sp) + { + ArchSpec::StopInfoOverrideCallbackType callback = GetProcess()->GetStopInfoOverrideCallback(); + if (callback) + callback(*this); + } + } } return m_stop_info_sp; } @@ -642,7 +662,8 @@ Thread::SetupForResume () lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext()); if (reg_ctx_sp) { - BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(reg_ctx_sp->GetPC()); + const addr_t thread_pc = reg_ctx_sp->GetPC(); + BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc); if (bp_site_sp) { // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target may not require anything @@ -650,19 +671,30 @@ Thread::SetupForResume () ThreadPlan *cur_plan = GetCurrentPlan(); - if (cur_plan->GetKind() != ThreadPlan::eKindStepOverBreakpoint) + bool push_step_over_bp_plan = false; + if (cur_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) { - ThreadPlanStepOverBreakpoint *step_bp_plan = new ThreadPlanStepOverBreakpoint (*this); - if (step_bp_plan) + ThreadPlanStepOverBreakpoint *bp_plan = (ThreadPlanStepOverBreakpoint *)cur_plan; + if (bp_plan->GetBreakpointLoadAddress() != thread_pc) + push_step_over_bp_plan = true; + } + else + push_step_over_bp_plan = true; + + if (push_step_over_bp_plan) + { + ThreadPlanSP step_bp_plan_sp (new ThreadPlanStepOverBreakpoint (*this)); + if (step_bp_plan_sp) { - ThreadPlanSP step_bp_plan_sp; - step_bp_plan->SetPrivate (true); + ; + step_bp_plan_sp->SetPrivate (true); if (GetCurrentPlan()->RunState() != eStateStepping) { + ThreadPlanStepOverBreakpoint *step_bp_plan + = static_cast<ThreadPlanStepOverBreakpoint *>(step_bp_plan_sp.get()); step_bp_plan->SetAutoContinue(true); } - step_bp_plan_sp.reset (step_bp_plan); QueueThreadPlan (step_bp_plan_sp, false); } } @@ -941,30 +973,30 @@ Thread::ShouldStop (Event* event_ptr) if (over_ride_stop) should_stop = false; - // One other potential problem is that we set up a master plan, then stop in before it is complete - for instance - // by hitting a breakpoint during a step-over - then do some step/finish/etc operations that wind up - // past the end point condition of the initial plan. We don't want to strand the original plan on the stack, - // This code clears stale plans off the stack. + } + + // One other potential problem is that we set up a master plan, then stop in before it is complete - for instance + // by hitting a breakpoint during a step-over - then do some step/finish/etc operations that wind up + // past the end point condition of the initial plan. We don't want to strand the original plan on the stack, + // This code clears stale plans off the stack. - if (should_stop) + if (should_stop) + { + ThreadPlan *plan_ptr = GetCurrentPlan(); + while (!PlanIsBasePlan(plan_ptr)) { - ThreadPlan *plan_ptr = GetCurrentPlan(); - while (!PlanIsBasePlan(plan_ptr)) - { - bool stale = plan_ptr->IsPlanStale (); - ThreadPlan *examined_plan = plan_ptr; - plan_ptr = GetPreviousPlan (examined_plan); + bool stale = plan_ptr->IsPlanStale (); + ThreadPlan *examined_plan = plan_ptr; + plan_ptr = GetPreviousPlan (examined_plan); - if (stale) - { - if (log) - log->Printf("Plan %s being discarded in cleanup, it says it is already done.", - examined_plan->GetName()); - DiscardThreadPlansUpToPlan(examined_plan); - } + if (stale) + { + if (log) + log->Printf("Plan %s being discarded in cleanup, it says it is already done.", + examined_plan->GetName()); + DiscardThreadPlansUpToPlan(examined_plan); } } - } if (log) @@ -1290,6 +1322,36 @@ Thread::SetTracer (lldb::ThreadPlanTracerSP &tracer_sp) m_plan_stack[i]->SetThreadPlanTracer(tracer_sp); } +bool +Thread::DiscardUserThreadPlansUpToIndex (uint32_t thread_index) +{ + // Count the user thread plans from the back end to get the number of the one we want + // to discard: + + uint32_t idx = 0; + ThreadPlan *up_to_plan_ptr = nullptr; + + for (ThreadPlanSP plan_sp : m_plan_stack) + { + if (plan_sp->GetPrivate()) + continue; + if (idx == thread_index) + { + up_to_plan_ptr = plan_sp.get(); + break; + } + else + idx++; + } + + if (up_to_plan_ptr == nullptr) + return false; + + DiscardThreadPlansUpToPlan(up_to_plan_ptr); + return true; +} + + void Thread::DiscardThreadPlansUpToPlan (lldb::ThreadPlanSP &up_to_plan_sp) { @@ -1483,18 +1545,16 @@ Thread::QueueThreadPlanForStepInRange LazyBool step_out_avoids_code_without_debug_info ) { - ThreadPlanSP thread_plan_sp; - ThreadPlanStepInRange *plan = new ThreadPlanStepInRange (*this, + ThreadPlanSP thread_plan_sp (new ThreadPlanStepInRange (*this, range, addr_context, stop_other_threads, step_in_avoids_code_without_debug_info, - step_out_avoids_code_without_debug_info); + step_out_avoids_code_without_debug_info)); + ThreadPlanStepInRange *plan = static_cast<ThreadPlanStepInRange *>(thread_plan_sp.get()); if (step_in_target) plan->SetStepInTarget(step_in_target); - - thread_plan_sp.reset (plan); QueueThreadPlan (thread_plan_sp, abort_other_plans); return thread_plan_sp; @@ -1546,17 +1606,18 @@ Thread::QueueThreadPlanForStepOutNoShouldStop uint32_t frame_idx ) { - ThreadPlanStepOut *new_plan = new ThreadPlanStepOut (*this, + ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut (*this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, - eLazyBoolNo); + eLazyBoolNo)); + + ThreadPlanStepOut *new_plan = static_cast<ThreadPlanStepOut *>(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); - ThreadPlanSP thread_plan_sp(new_plan); - + if (thread_plan_sp->ValidatePlan(NULL)) { QueueThreadPlan (thread_plan_sp, abort_other_plans); @@ -1602,61 +1663,105 @@ Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, } +lldb::ThreadPlanSP +Thread::QueueThreadPlanForStepScripted (bool abort_other_plans, + const char *class_name, + bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanPython (*this, class_name)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + // This seems a little funny, but I don't want to have to split up the constructor and the + // DidPush in the scripted plan, that seems annoying. + // That means the constructor has to be in DidPush. + // So I have to validate the plan AFTER pushing it, and then take it off again... + if (!thread_plan_sp->ValidatePlan(nullptr)) + { + DiscardThreadPlansUpToPlan(thread_plan_sp); + return ThreadPlanSP(); + } + else + return thread_plan_sp; + +} + uint32_t Thread::GetIndexID () const { return m_index_id; } -void -Thread::DumpThreadPlans (lldb_private::Stream *s) const +static void +PrintPlanElement (Stream *s, const ThreadPlanSP &plan, lldb::DescriptionLevel desc_level, int32_t elem_idx) { - uint32_t stack_size = m_plan_stack.size(); - int i; - s->Indent(); - s->Printf ("Plan Stack for thread #%u: tid = 0x%4.4" PRIx64 ", stack_size = %d\n", GetIndexID(), GetID(), stack_size); - for (i = stack_size - 1; i >= 0; i--) - { s->IndentMore(); s->Indent(); - s->Printf ("Element %d: ", i); - m_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->Printf ("Element %d: ", elem_idx); + plan->GetDescription (s, desc_level); s->EOL(); s->IndentLess(); +} + +static void +PrintPlanStack (Stream *s, const std::vector<lldb::ThreadPlanSP> &plan_stack, lldb::DescriptionLevel desc_level, bool include_internal) +{ + int32_t print_idx = 0; + for (ThreadPlanSP plan_sp : plan_stack) + { + if (include_internal || !plan_sp->GetPrivate()) + { + PrintPlanElement (s, plan_sp, desc_level, print_idx++); + } } +} - stack_size = m_completed_plan_stack.size(); - if (stack_size > 0) +void +Thread::DumpThreadPlans (Stream *s, + lldb::DescriptionLevel desc_level, + bool include_internal, + bool ignore_boring_threads) const +{ + uint32_t stack_size; + + if (ignore_boring_threads) { - s->Indent(); - s->Printf ("Completed Plan Stack: %d elements.\n", stack_size); - for (i = stack_size - 1; i >= 0; i--) + uint32_t stack_size = m_plan_stack.size(); + uint32_t completed_stack_size = m_completed_plan_stack.size(); + uint32_t discarded_stack_size = m_discarded_plan_stack.size(); + if (stack_size == 1 && completed_stack_size == 0 && discarded_stack_size == 0) { + s->Printf ("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID()); s->IndentMore(); s->Indent(); - s->Printf ("Element %d: ", i); - m_completed_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); - s->EOL(); + s->Printf("No active thread plans\n"); s->IndentLess(); + return; } } + s->Indent(); + s->Printf ("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID()); + s->IndentMore(); + s->Indent(); + s->Printf ("Active plan stack:\n"); + PrintPlanStack (s, m_plan_stack, desc_level, include_internal); + + stack_size = m_completed_plan_stack.size(); + if (stack_size > 0) + { + s->Indent(); + s->Printf ("Completed Plan Stack:\n"); + PrintPlanStack (s, m_completed_plan_stack, desc_level, include_internal); + } + stack_size = m_discarded_plan_stack.size(); if (stack_size > 0) { s->Indent(); - s->Printf ("Discarded Plan Stack: %d elements.\n", stack_size); - for (i = stack_size - 1; i >= 0; i--) - { - s->IndentMore(); - s->Indent(); - s->Printf ("Element %d: ", i); - m_discarded_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); - s->EOL(); - s->IndentLess(); - } + s->Printf ("Discarded Plan Stack:\n"); + PrintPlanStack (s, m_discarded_plan_stack, desc_level, include_internal); } + s->IndentLess(); } TargetSP @@ -1785,7 +1890,7 @@ Thread::ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return // FIXME: ValueObject::Cast doesn't currently work correctly, at least not for scalars. // Turn that back on when that works. - if (0 && sc.function != NULL) + if (/* DISABLES CODE */ (0) && sc.function != NULL) { Type *function_type = sc.function->GetType(); if (function_type) @@ -2011,6 +2116,7 @@ Thread::StopReasonAsCString (lldb::StopReason reason) case eStopReasonExec: return "exec"; case eStopReasonPlanComplete: return "plan complete"; case eStopReasonThreadExiting: return "thread exiting"; + case eStopReasonInstrumentation: return "instrumentation break"; } @@ -2090,17 +2196,28 @@ Thread::GetStatus (Stream &strm, uint32_t start_frame, uint32_t num_frames, uint } bool -Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json) +Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo) { DumpUsingSettingsFormat (strm, 0); strm.Printf("\n"); StructuredData::ObjectSP thread_info = GetExtendedInfo(); - - if (thread_info && print_json) + StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo(); + + if (print_json_thread || print_json_stopinfo) { - thread_info->Dump (strm); - strm.Printf("\n"); + if (thread_info && print_json_thread) + { + thread_info->Dump (strm); + strm.Printf("\n"); + } + + if (stop_info && print_json_stopinfo) + { + stop_info->Dump (strm); + strm.Printf("\n"); + } + return true; } @@ -2194,6 +2311,8 @@ Thread::GetUnwinder () case llvm::Triple::aarch64: case llvm::Triple::thumb: case llvm::Triple::mips64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: case llvm::Triple::hexagon: m_unwinder_ap.reset (new UnwindLLDB (*this)); break; diff --git a/source/Target/ThreadCollection.cpp b/source/Target/ThreadCollection.cpp new file mode 100644 index 0000000000000..dc1e38e02420e --- /dev/null +++ b/source/Target/ThreadCollection.cpp @@ -0,0 +1,62 @@ +//===-- ThreadCollection.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include <stdlib.h> + +#include <algorithm> + +#include "lldb/Target/ThreadCollection.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadCollection::ThreadCollection() : + m_threads(), + m_mutex() +{ +} + +ThreadCollection::ThreadCollection(collection threads) : + m_threads(threads), + m_mutex() +{ +} + +void +ThreadCollection::AddThread (const ThreadSP &thread_sp) +{ + Mutex::Locker locker(GetMutex()); + m_threads.push_back (thread_sp); +} + +void +ThreadCollection::InsertThread (const lldb::ThreadSP &thread_sp, uint32_t idx) +{ + Mutex::Locker locker(GetMutex()); + if (idx < m_threads.size()) + m_threads.insert(m_threads.begin() + idx, thread_sp); + else + m_threads.push_back (thread_sp); +} + +uint32_t +ThreadCollection::GetSize () +{ + Mutex::Locker locker(GetMutex()); + return m_threads.size(); +} + +ThreadSP +ThreadCollection::GetThreadAtIndex (uint32_t idx) +{ + Mutex::Locker locker(GetMutex()); + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} diff --git a/source/Target/ThreadList.cpp b/source/Target/ThreadList.cpp index 7fb16fdac5c03..db4407b4b5791 100644 --- a/source/Target/ThreadList.cpp +++ b/source/Target/ThreadList.cpp @@ -22,17 +22,17 @@ using namespace lldb; using namespace lldb_private; ThreadList::ThreadList (Process *process) : + ThreadCollection(), m_process (process), m_stop_id (0), - m_threads(), m_selected_tid (LLDB_INVALID_THREAD_ID) { } ThreadList::ThreadList (const ThreadList &rhs) : + ThreadCollection(), m_process (rhs.m_process), m_stop_id (rhs.m_stop_id), - m_threads (), m_selected_tid () { // Use the assignment operator since it uses the mutex @@ -77,25 +77,6 @@ ThreadList::SetStopID (uint32_t stop_id) m_stop_id = stop_id; } - -void -ThreadList::AddThread (const ThreadSP &thread_sp) -{ - Mutex::Locker locker(GetMutex()); - m_threads.push_back(thread_sp); -} - -void -ThreadList::InsertThread (const lldb::ThreadSP &thread_sp, uint32_t idx) -{ - Mutex::Locker locker(GetMutex()); - if (idx < m_threads.size()) - m_threads.insert(m_threads.begin() + idx, thread_sp); - else - m_threads.push_back (thread_sp); -} - - uint32_t ThreadList::GetSize (bool can_update) { diff --git a/source/Target/ThreadPlanPython.cpp b/source/Target/ThreadPlanPython.cpp new file mode 100644 index 0000000000000..e196d81c897a1 --- /dev/null +++ b/source/Target/ThreadPlanPython.cpp @@ -0,0 +1,192 @@ +//===-- ThreadPlan.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/Target/ThreadPlan.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanPython.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanPython +//---------------------------------------------------------------------- + +ThreadPlanPython::ThreadPlanPython (Thread &thread, const char *class_name) : + ThreadPlan (ThreadPlan::eKindPython, + "Python based Thread Plan", + thread, + eVoteNoOpinion, + eVoteNoOpinion), + m_class_name (class_name) +{ + SetIsMasterPlan (true); + SetOkayToDiscard (true); + SetPrivate (false); +} + +ThreadPlanPython::~ThreadPlanPython () +{ + // FIXME, do I need to decrement the ref count on this implementation object to make it go away? +} + +bool +ThreadPlanPython::ValidatePlan (Stream *error) +{ + // I have to postpone setting up the implementation till after the constructor because I need to call + // shared_from_this, which you can't do in the constructor. So I'll do it here. + if (m_implementation_sp) + return true; + else + return false; +} + +void +ThreadPlanPython::DidPush() +{ + // We set up the script side in DidPush, so that it can push other plans in the constructor, + // and doesn't have to care about the details of DidPush. + + if (!m_class_name.empty()) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + m_implementation_sp = script_interp->CreateScriptedThreadPlan (m_class_name.c_str(), this->shared_from_this()); + } + } +} + +bool +ThreadPlanPython::ShouldStop (Event *event_ptr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + + bool should_stop = true; + if (m_implementation_sp) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + bool script_error; + should_stop = script_interp->ScriptedThreadPlanShouldStop (m_implementation_sp, event_ptr, script_error); + if (script_error) + SetPlanComplete(false); + } + } + return should_stop; +} + +bool +ThreadPlanPython::DoPlanExplainsStop (Event *event_ptr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + + bool explains_stop = true; + if (m_implementation_sp) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + bool script_error; + explains_stop = script_interp->ScriptedThreadPlanExplainsStop (m_implementation_sp, event_ptr, script_error); + if (script_error) + SetPlanComplete(false); + } + } + return explains_stop; +} + +bool +ThreadPlanPython::MischiefManaged () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + bool mischief_managed = true; + if (m_implementation_sp) + { + // I don't really need mischief_managed, since it's simpler to just call SetPlanComplete in should_stop. + mischief_managed = IsPlanComplete(); + if (mischief_managed) + m_implementation_sp.reset(); + } + return mischief_managed; +} + +lldb::StateType +ThreadPlanPython::GetPlanRunState () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, + m_class_name.c_str()); + lldb::StateType run_state = eStateRunning; + if (m_implementation_sp) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + bool script_error; + run_state = script_interp->ScriptedThreadPlanGetRunState (m_implementation_sp, script_error); + } + } + return run_state; +} + +// The ones below are not currently exported to Python. + +bool +ThreadPlanPython::StopOthers () +{ + // For now Python plans run all threads, but we should add some controls for this. + return false; +} + +void +ThreadPlanPython::GetDescription (Stream *s, + lldb::DescriptionLevel level) +{ + s->Printf ("Python thread plan implemented by class %s.", m_class_name.c_str()); +} + +bool +ThreadPlanPython::WillStop () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + return true; +} diff --git a/source/Target/ThreadPlanStepInRange.cpp b/source/Target/ThreadPlanStepInRange.cpp index 3e9abef65573d..e5f057c183fbe 100644 --- a/source/Target/ThreadPlanStepInRange.cpp +++ b/source/Target/ThreadPlanStepInRange.cpp @@ -105,7 +105,6 @@ ThreadPlanStepInRange::SetupAvoidNoDebug(LazyBool step_in_avoids_code_without_de else GetFlags().Clear (ThreadPlanShouldStopHere::eStepInAvoidNoDebug); - avoid_nodebug = true; switch (step_out_avoids_code_without_debug_info) { case eLazyBoolYes: @@ -128,17 +127,31 @@ void ThreadPlanStepInRange::GetDescription (Stream *s, lldb::DescriptionLevel level) { if (level == lldb::eDescriptionLevelBrief) + { s->Printf("step in"); - else + return; + } + + s->Printf ("Stepping in"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) + { + s->Printf (" through line "); + m_addr_context.line_entry.DumpStopContext (s, false); + printed_line_info = true; + } + + const char *step_into_target = m_step_into_target.AsCString(); + if (step_into_target && step_into_target[0] != '\0') + s->Printf (" targeting %s", m_step_into_target.AsCString()); + + if (!printed_line_info || level == eDescriptionLevelVerbose) { - s->Printf ("Stepping through range (stepping into functions): "); + s->Printf (" using ranges:"); DumpRanges(s); - const char *step_into_target = m_step_into_target.AsCString(); - if (step_into_target && step_into_target[0] != '\0') - s->Printf (" targeting %s.", m_step_into_target.AsCString()); - else - s->PutChar('.'); } + + s->PutChar('.'); } bool @@ -303,6 +316,7 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr) else { m_no_more_plans = false; + m_sub_plan_sp->SetPrivate(true); return false; } } diff --git a/source/Target/ThreadPlanStepInstruction.cpp b/source/Target/ThreadPlanStepInstruction.cpp index fabf63b1e9d64..0f6d7b78a9cee 100644 --- a/source/Target/ThreadPlanStepInstruction.cpp +++ b/source/Target/ThreadPlanStepInstruction.cpp @@ -150,7 +150,16 @@ ThreadPlanStepInstruction::ShouldStop (Event *event_ptr) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - StackID cur_frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + StackFrameSP cur_frame_sp = m_thread.GetStackFrameAtIndex(0); + if (!cur_frame_sp) + { + if (log) + log->Printf ("ThreadPlanStepInstruction couldn't get the 0th frame, stopping."); + SetPlanComplete(); + return true; + } + + StackID cur_frame_zero_id = cur_frame_sp->GetStackID(); if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id) { @@ -180,6 +189,24 @@ ThreadPlanStepInstruction::ShouldStop (Event *event_ptr) { if (return_frame->GetStackID() != m_parent_frame_id || m_start_has_symbol) { + // next-instruction shouldn't step out of inlined functions. But we may have stepped into a + // real function that starts with an inlined function, and we do want to step out of that... + + if (cur_frame_sp->IsInlined()) + { + StackFrameSP parent_frame_sp = m_thread.GetFrameWithStackID(m_stack_id); + + if(parent_frame_sp && parent_frame_sp->GetConcreteFrameIndex() == cur_frame_sp->GetConcreteFrameIndex()) + { + SetPlanComplete(); + if (log) + { + log->Printf("Frame we stepped into is inlined into the frame we were stepping from, stopping."); + } + return true; + } + } + if (log) { StreamString s; diff --git a/source/Target/ThreadPlanStepOut.cpp b/source/Target/ThreadPlanStepOut.cpp index b62f557319a81..0ded99b3091d5 100644 --- a/source/Target/ThreadPlanStepOut.cpp +++ b/source/Target/ThreadPlanStepOut.cpp @@ -54,7 +54,6 @@ ThreadPlanStepOut::ThreadPlanStepOut m_return_addr (LLDB_INVALID_ADDRESS), m_stop_others (stop_others), m_immediate_step_from_function(NULL) - { SetFlagsToDefault(); SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); @@ -90,6 +89,7 @@ ThreadPlanStepOut::ThreadPlanStepOut frame_idx - 1, eLazyBoolNo)); static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())->SetShouldStopHereCallbacks(nullptr, nullptr); + m_step_out_to_inline_plan_sp->SetPrivate(true); } else { @@ -177,10 +177,34 @@ ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level) else if (m_step_through_inline_plan_sp) s->Printf ("Stepping out by stepping through inlined function."); else - s->Printf ("Stepping out from address 0x%" PRIx64 " to return address 0x%" PRIx64 " using breakpoint site %d", - (uint64_t)m_step_from_insn, - (uint64_t)m_return_addr, - m_return_bp_id); + { + s->Printf ("Stepping out from "); + Address tmp_address; + if (tmp_address.SetLoadAddress (m_step_from_insn, &GetTarget())) + { + tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription, Address::DumpStyleLoadAddress); + } + else + { + s->Printf ("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn); + } + + // FIXME: find some useful way to present the m_return_id, since there may be multiple copies of the + // same function on the stack. + + s->Printf ("returning to frame at "); + if (tmp_address.SetLoadAddress (m_return_addr, &GetTarget())) + { + tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription, Address::DumpStyleLoadAddress); + } + else + { + s->Printf ("address 0x%" PRIx64 "", (uint64_t)m_return_addr); + } + + if (level == eDescriptionLevelVerbose) + s->Printf(" using breakpoint site %d", m_return_bp_id); + } } } @@ -474,11 +498,16 @@ ThreadPlanStepOut::QueueInlinedStepPlan (bool queue_now) inlined_sc.target_sp = GetTarget().shared_from_this(); RunMode run_mode = m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads; const LazyBool avoid_no_debug = eLazyBoolNo; - ThreadPlanStepOverRange *step_through_inline_plan_ptr = new ThreadPlanStepOverRange(m_thread, - inline_range, - inlined_sc, - run_mode, - avoid_no_debug); + + m_step_through_inline_plan_sp.reset (new ThreadPlanStepOverRange(m_thread, + inline_range, + inlined_sc, + run_mode, + avoid_no_debug)); + ThreadPlanStepOverRange *step_through_inline_plan_ptr + = static_cast<ThreadPlanStepOverRange *>(m_step_through_inline_plan_sp.get()); + m_step_through_inline_plan_sp->SetPrivate(true); + step_through_inline_plan_ptr->SetOkayToDiscard(true); StreamString errors; if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) @@ -493,7 +522,7 @@ ThreadPlanStepOut::QueueInlinedStepPlan (bool queue_now) if (inlined_block->GetRangeAtIndex (i, inline_range)) step_through_inline_plan_ptr->AddRange (inline_range); } - m_step_through_inline_plan_sp.reset (step_through_inline_plan_ptr); + if (queue_now) m_thread.QueueThreadPlan (m_step_through_inline_plan_sp, false); return true; diff --git a/source/Target/ThreadPlanStepOverBreakpoint.cpp b/source/Target/ThreadPlanStepOverBreakpoint.cpp index dc011e5454023..6f285e25fd0b6 100644 --- a/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -64,11 +64,33 @@ ThreadPlanStepOverBreakpoint::DoPlanExplainsStop (Event *event_ptr) StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { + // It's a little surprising that we stop here for a breakpoint hit. However, when you single step ONTO a breakpoint + // we still want to call that a breakpoint hit, and trigger the actions, etc. Otherwise you would see the + // PC at the breakpoint without having triggered the actions, then you'd continue, the PC wouldn't change, + // and you'd see the breakpoint hit, which would be odd. + // So the lower levels fake "step onto breakpoint address" and return that as a breakpoint. So our trace + // step COULD appear as a breakpoint hit if the next instruction also contained a breakpoint. StopReason reason = stop_info_sp->GetStopReason(); - if (reason == eStopReasonTrace || reason == eStopReasonNone) + + switch (reason) + { + case eStopReasonTrace: + case eStopReasonNone: return true; - else + case eStopReasonBreakpoint: + // It's a little surprising that we stop here for a breakpoint hit. However, when you single step ONTO a + // breakpoint we still want to call that a breakpoint hit, and trigger the actions, etc. Otherwise you + // would see the PC at the breakpoint without having triggered the actions, then you'd continue, the PC + // wouldn't change, and you'd see the breakpoint hit, which would be odd. + // So the lower levels fake "step onto breakpoint address" and return that as a breakpoint hit. So our trace + // step COULD appear as a breakpoint hit if the next instruction also contained a breakpoint. We don't want + // to handle that, since we really don't know what to do with breakpoint hits. But make sure we don't set + // ourselves to auto-continue or we'll wrench control away from the plans that can deal with this. + SetAutoContinue(false); + return false; + default: return false; + } } return false; } @@ -76,7 +98,7 @@ ThreadPlanStepOverBreakpoint::DoPlanExplainsStop (Event *event_ptr) bool ThreadPlanStepOverBreakpoint::ShouldStop (Event *event_ptr) { - return false; + return !ShouldAutoContinue(event_ptr); } bool @@ -163,3 +185,10 @@ ThreadPlanStepOverBreakpoint::ShouldAutoContinue (Event *event_ptr) { return m_auto_continue; } + +bool +ThreadPlanStepOverBreakpoint::IsPlanStale() +{ + return m_thread.GetRegisterContext()->GetPC() != m_breakpoint_addr; +} + diff --git a/source/Target/ThreadPlanStepOverRange.cpp b/source/Target/ThreadPlanStepOverRange.cpp index a4f3743346e21..a6d65e4d81164 100644 --- a/source/Target/ThreadPlanStepOverRange.cpp +++ b/source/Target/ThreadPlanStepOverRange.cpp @@ -62,12 +62,26 @@ void ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level) { if (level == lldb::eDescriptionLevelBrief) + { s->Printf("step over"); - else + return; + } + s->Printf ("Stepping over"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) + { + s->Printf (" line "); + m_addr_context.line_entry.DumpStopContext (s, false); + printed_line_info = true; + } + + if (!printed_line_info || level == eDescriptionLevelVerbose) { - s->Printf ("stepping through range (stepping over functions): "); - DumpRanges(s); + s->Printf (" using ranges: "); + DumpRanges(s); } + + s->PutChar('.'); } void @@ -317,11 +331,15 @@ ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) { new_plan_sp = CheckShouldStopHereAndQueueStepOut (frame_order); } - + if (!new_plan_sp) m_no_more_plans = true; else + { + // Any new plan will be an implementation plan, so mark it private: + new_plan_sp->SetPrivate(true); m_no_more_plans = false; + } if (!new_plan_sp) { diff --git a/source/Target/ThreadPlanStepRange.cpp b/source/Target/ThreadPlanStepRange.cpp index 82ca59fbca39f..adc515cebc08e 100644 --- a/source/Target/ThreadPlanStepRange.cpp +++ b/source/Target/ThreadPlanStepRange.cpp @@ -44,7 +44,8 @@ ThreadPlanStepRange::ThreadPlanStepRange (ThreadPlanKind kind, Thread &thread, const AddressRange &range, const SymbolContext &addr_context, - lldb::RunMode stop_others) : + lldb::RunMode stop_others, + bool given_ranges_only) : ThreadPlan (kind, name, thread, eVoteNoOpinion, eVoteNoOpinion), m_addr_context (addr_context), m_address_ranges (), @@ -53,7 +54,8 @@ ThreadPlanStepRange::ThreadPlanStepRange (ThreadPlanKind kind, m_parent_stack_id(), m_no_more_plans (false), m_first_run_event (true), - m_use_fast_step(false) + m_use_fast_step(false), + m_given_ranges_only (given_ranges_only) { m_use_fast_step = GetTarget().GetUseFastStepping(); AddRange(range); @@ -149,7 +151,7 @@ ThreadPlanStepRange::InRange () break; } - if (!ret_value) + if (!ret_value && !m_given_ranges_only) { // See if we've just stepped to another part of the same line number... StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get(); diff --git a/source/Target/ThreadPlanStepThrough.cpp b/source/Target/ThreadPlanStepThrough.cpp index 18da6cdbd59cf..5b50a41a6398d 100644 --- a/source/Target/ThreadPlanStepThrough.cpp +++ b/source/Target/ThreadPlanStepThrough.cpp @@ -87,7 +87,10 @@ ThreadPlanStepThrough::DidPush () void ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC() { - m_sub_plan_sp = m_thread.GetProcess()->GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others); + DynamicLoader *loader = m_thread.GetProcess()->GetDynamicLoader(); + if (loader) + m_sub_plan_sp = loader->GetStepThroughTrampolinePlan (m_thread, m_stop_others); + // If that didn't come up with anything, try the ObjC runtime plugin: if (!m_sub_plan_sp.get()) { diff --git a/source/Target/ThreadPlanTracer.cpp b/source/Target/ThreadPlanTracer.cpp index 5188df6d91936..d2b039d69f673 100644 --- a/source/Target/ThreadPlanTracer.cpp +++ b/source/Target/ThreadPlanTracer.cpp @@ -212,11 +212,15 @@ ThreadPlanAssemblyTracer::Log () const bool show_bytes = true; const bool show_address = true; Instruction *instruction = instruction_list.GetInstructionAtIndex(0).get(); + const char *disassemble_format = "${addr-file-or-load}: "; instruction->Dump (stream, max_opcode_byte_size, show_address, show_bytes, - NULL); + NULL, + NULL, + NULL, + disassemble_format); } } } |