diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 |
commit | 94994d372d014ce4c8758b9605d63fae651bd8aa (patch) | |
tree | 51c0b708bd59f205d6b35cb2a8c24d62f0c33d77 /source/Target | |
parent | 39be7ce23363d12ae3e49aeb1fdb2bfeb892e836 (diff) | |
download | src-94994d372d014ce4c8758b9605d63fae651bd8aa.tar.gz src-94994d372d014ce4c8758b9605d63fae651bd8aa.zip |
Vendor import of lldb trunk r351319 (just before the release_80 branchvendor/lldb/lldb-trunk-r351319
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=343181
svn path=/vendor/lldb/lldb-trunk-r351319/; revision=343182; tag=vendor/lldb/lldb-trunk-r351319
Diffstat (limited to 'source/Target')
55 files changed, 2008 insertions, 1346 deletions
diff --git a/source/Target/ABI.cpp b/source/Target/ABI.cpp index 8d4513ad6811..96e09b75094a 100644 --- a/source/Target/ABI.cpp +++ b/source/Target/ABI.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ABI.h" #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" #include "lldb/Core/PluginManager.h" diff --git a/source/Target/CMakeLists.txt b/source/Target/CMakeLists.txt index df137c314afa..265b3a80779d 100644 --- a/source/Target/CMakeLists.txt +++ b/source/Target/CMakeLists.txt @@ -28,6 +28,7 @@ add_lldb_library(lldbTarget SectionLoadList.cpp StackFrame.cpp StackFrameList.cpp + StackFrameRecognizer.cpp StackID.cpp StopInfo.cpp StructuredDataPlugin.cpp diff --git a/source/Target/CPPLanguageRuntime.cpp b/source/Target/CPPLanguageRuntime.cpp index c7e7b2511971..f0bd1758461c 100644 --- a/source/Target/CPPLanguageRuntime.cpp +++ b/source/Target/CPPLanguageRuntime.cpp @@ -14,9 +14,19 @@ #include "llvm/ADT/StringRef.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/VariableList.h" + #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/ThreadPlanStepInRange.h" using namespace lldb; using namespace lldb_private; @@ -40,3 +50,297 @@ bool CPPLanguageRuntime::GetObjectDescription( // C++ has no generic way to do this. return false; } + +CPPLanguageRuntime::LibCppStdFunctionCallableInfo +CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( + lldb::ValueObjectSP &valobj_sp) { + LibCppStdFunctionCallableInfo optional_info; + + if (!valobj_sp) + return optional_info; + + // Member __f_ has type __base*, the contents of which will hold: + // 1) a vtable entry which may hold type information needed to discover the + // lambda being called + // 2) possibly hold a pointer to the callable object + // e.g. + // + // (lldb) frame var -R f_display + // (std::__1::function<void (int)>) f_display = { + // __buf_ = { + // … + // } + // __f_ = 0x00007ffeefbffa00 + // } + // (lldb) memory read -fA 0x00007ffeefbffa00 + // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ... + // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ... + // + // We will be handling five cases below, std::function is wrapping: + // + // 1) a lambda we know at compile time. We will obtain the name of the lambda + // from the first template pameter from __func's vtable. We will look up + // the lambda's operator()() and obtain the line table entry. + // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method + // will be stored after the vtable. We will obtain the lambdas name from + // this entry and lookup operator()() and obtain the line table entry. + // 3) a callable object via operator()(). We will obtain the name of the + // object from the first template parameter from __func's vtable. We will + // look up the objectc operator()() and obtain the line table entry. + // 4) a member function. A pointer to the function will stored after the + // we will obtain the name from this pointer. + // 5) a free function. A pointer to the function will stored after the vtable + // we will obtain the name from this pointer. + ValueObjectSP member__f_( + valobj_sp->GetChildMemberWithName(ConstString("__f_"), true)); + + if (member__f_) { + ValueObjectSP sub_member__f_( + member__f_->GetChildMemberWithName(ConstString("__f_"), true)); + + if (sub_member__f_) + member__f_ = sub_member__f_; + } + + lldb::addr_t member__f_pointer_value = member__f_->GetValueAsUnsigned(0); + + optional_info.member__f_pointer_value = member__f_pointer_value; + + ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + + if (process == nullptr) + return optional_info; + + uint32_t address_size = process->GetAddressByteSize(); + Status status; + + // First item pointed to by __f_ should be the pointer to the vtable for + // a __base object. + lldb::addr_t vtable_address = + process->ReadPointerFromMemory(member__f_pointer_value, status); + + if (status.Fail()) + return optional_info; + + lldb::addr_t address_after_vtable = member__f_pointer_value + address_size; + // As commened above we may not have a function pointer but if we do we will + // need it. + lldb::addr_t possible_function_address = + process->ReadPointerFromMemory(address_after_vtable, status); + + if (status.Fail()) + return optional_info; + + Target &target = process->GetTarget(); + + if (target.GetSectionLoadList().IsEmpty()) + return optional_info; + + Address vtable_addr_resolved; + SymbolContext sc; + Symbol *symbol; + + if (!target.GetSectionLoadList().ResolveLoadAddress(vtable_address, + vtable_addr_resolved)) + return optional_info; + + target.GetImages().ResolveSymbolContextForAddress( + vtable_addr_resolved, eSymbolContextEverything, sc); + symbol = sc.symbol; + + if (symbol == nullptr) + return optional_info; + + llvm::StringRef vtable_name(symbol->GetName().GetCString()); + bool found_expected_start_string = + vtable_name.startswith("vtable for std::__1::__function::__func<"); + + if (!found_expected_start_string) + return optional_info; + + // Given case 1 or 3 we have a vtable name, we are want to extract the first + // template parameter + // + // ... __func<main::$_0, std::__1::allocator<main::$_0> ... + // ^^^^^^^^^ + // + // We do this by find the first < and , and extracting in between. + // + // This covers the case of the lambda known at compile time. + size_t first_open_angle_bracket = vtable_name.find('<') + 1; + size_t first_comma = vtable_name.find_first_of(','); + + llvm::StringRef first_template_parameter = + vtable_name.slice(first_open_angle_bracket, first_comma); + + Address function_address_resolved; + + // Setup for cases 2, 4 and 5 we have a pointer to a function after the + // vtable. We will use a process of elimination to drop through each case + // and obtain the data we need. + if (target.GetSectionLoadList().ResolveLoadAddress( + possible_function_address, function_address_resolved)) { + target.GetImages().ResolveSymbolContextForAddress( + function_address_resolved, eSymbolContextEverything, sc); + symbol = sc.symbol; + } + + auto get_name = [&first_template_parameter, &symbol]() { + // Given case 1: + // + // main::$_0 + // + // we want to append ::operator()() + if (first_template_parameter.contains("$_")) + return llvm::Regex::escape(first_template_parameter.str()) + + R"(::operator\(\)\(.*\))"; + + if (symbol != NULL && + symbol->GetName().GetStringRef().contains("__invoke")) { + + llvm::StringRef symbol_name = symbol->GetName().GetStringRef(); + size_t pos2 = symbol_name.find_last_of(':'); + + // Given case 2: + // + // main::$_1::__invoke(...) + // + // We want to slice off __invoke(...) and append operator()() + std::string lambda_operator = + llvm::Regex::escape(symbol_name.slice(0, pos2 + 1).str()) + + R"(operator\(\)\(.*\))"; + + return lambda_operator; + } + + // Case 3 + return first_template_parameter.str() + R"(::operator\(\)\(.*\))"; + ; + }; + + std::string func_to_match = get_name(); + + SymbolContextList scl; + + target.GetImages().FindFunctions(RegularExpression{func_to_match}, true, true, + true, scl); + + // Case 1,2 or 3 + if (scl.GetSize() >= 1) { + SymbolContext sc2 = scl[0]; + + AddressRange range; + sc2.GetAddressRange(eSymbolContextEverything, 0, false, range); + + Address address = range.GetBaseAddress(); + + Address addr; + if (target.ResolveLoadAddress(address.GetCallableLoadAddress(&target), + addr)) { + LineEntry line_entry; + addr.CalculateSymbolContextLineEntry(line_entry); + + if (first_template_parameter.contains("$_") || + (symbol != nullptr && + symbol->GetName().GetStringRef().contains("__invoke"))) { + // Case 1 and 2 + optional_info.callable_case = LibCppStdFunctionCallableCase::Lambda; + } else { + // Case 3 + optional_info.callable_case = + LibCppStdFunctionCallableCase::CallableObject; + } + + optional_info.callable_symbol = *symbol; + optional_info.callable_line_entry = line_entry; + optional_info.callable_address = addr; + return optional_info; + } + } + + // Case 4 or 5 + if (!symbol->GetName().GetStringRef().startswith("vtable for")) { + optional_info.callable_case = + LibCppStdFunctionCallableCase::FreeOrMemberFunction; + optional_info.callable_address = function_address_resolved; + optional_info.callable_symbol = *symbol; + + return optional_info; + } + + return optional_info; +} + +lldb::ThreadPlanSP +CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread, + bool stop_others) { + ThreadPlanSP ret_plan_sp; + + lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + + TargetSP target_sp(thread.CalculateTarget()); + + if (target_sp->GetSectionLoadList().IsEmpty()) + return ret_plan_sp; + + Address pc_addr_resolved; + SymbolContext sc; + Symbol *symbol; + + if (!target_sp->GetSectionLoadList().ResolveLoadAddress(curr_pc, + pc_addr_resolved)) + return ret_plan_sp; + + target_sp->GetImages().ResolveSymbolContextForAddress( + pc_addr_resolved, eSymbolContextEverything, sc); + symbol = sc.symbol; + + if (symbol == nullptr) + return ret_plan_sp; + + llvm::StringRef function_name(symbol->GetName().GetCString()); + + // Handling the case where we are attempting to step into std::function. + // The behavior will be that we will attempt to obtain the wrapped + // callable via FindLibCppStdFunctionCallableInfo() and if we find it we + // will return a ThreadPlanRunToAddress to the callable. Therefore we will + // step into the wrapped callable. + // + bool found_expected_start_string = + function_name.startswith("std::__1::function<"); + + if (!found_expected_start_string) + return ret_plan_sp; + + AddressRange range_of_curr_func; + sc.GetAddressRange(eSymbolContextEverything, 0, false, range_of_curr_func); + + StackFrameSP frame = thread.GetStackFrameAtIndex(0); + + if (frame) { + ValueObjectSP value_sp = frame->FindVariable(ConstString("this")); + + CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = + FindLibCppStdFunctionCallableInfo(value_sp); + + if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid && + value_sp->GetValueIsValid()) { + // We found the std::function wrapped callable and we have its address. + // We now create a ThreadPlan to run to the callable. + ret_plan_sp.reset(new ThreadPlanRunToAddress( + thread, callable_info.callable_address, stop_others)); + return ret_plan_sp; + } else { + // We are in std::function but we could not obtain the callable. + // We create a ThreadPlan to keep stepping through using the address range + // of the current function. + ret_plan_sp.reset(new ThreadPlanStepInRange(thread, range_of_curr_func, + sc, eOnlyThisThread, + eLazyBoolYes, eLazyBoolYes)); + return ret_plan_sp; + } + } + + return ret_plan_sp; +} diff --git a/source/Target/ExecutionContext.cpp b/source/Target/ExecutionContext.cpp index 73c64916cf1f..7f1f0331c922 100644 --- a/source/Target/ExecutionContext.cpp +++ b/source/Target/ExecutionContext.cpp @@ -7,17 +7,13 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ExecutionContext.h" -#include "lldb/Core/State.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/State.h" using namespace lldb_private; diff --git a/source/Target/InstrumentationRuntime.cpp b/source/Target/InstrumentationRuntime.cpp index ac8b5dfe4599..e948d43d50af 100644 --- a/source/Target/InstrumentationRuntime.cpp +++ b/source/Target/InstrumentationRuntime.cpp @@ -7,10 +7,6 @@ // //===---------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" diff --git a/source/Target/JITLoader.cpp b/source/Target/JITLoader.cpp index 77ce4add486f..ddadcfa87a0a 100644 --- a/source/Target/JITLoader.cpp +++ b/source/Target/JITLoader.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/JITLoader.h" #include "lldb/Core/PluginManager.h" #include "lldb/Target/JITLoaderList.h" diff --git a/source/Target/Language.cpp b/source/Target/Language.cpp index cde6f8654aec..5c7a4097dd6f 100644 --- a/source/Target/Language.cpp +++ b/source/Target/Language.cpp @@ -77,7 +77,39 @@ Language *Language::FindPlugin(lldb::LanguageType language) { return nullptr; } +Language *Language::FindPlugin(llvm::StringRef file_path) { + Language *result = nullptr; + ForEach([&result, file_path](Language *language) { + if (language->IsSourceFile(file_path)) { + result = language; + return false; + } + return true; + }); + return result; +} + +Language *Language::FindPlugin(LanguageType language, + llvm::StringRef file_path) { + Language *result = FindPlugin(language); + // Finding a language by file path is slower, we so we use this as the + // fallback. + if (!result) + result = FindPlugin(file_path); + return result; +} + void Language::ForEach(std::function<bool(Language *)> callback) { + // If we want to iterate over all languages, we first have to complete the + // LanguagesMap. + static llvm::once_flag g_initialize; + llvm::call_once(g_initialize, [] { + for (unsigned lang = eLanguageTypeUnknown; lang < eNumLanguageTypes; + ++lang) { + FindPlugin(static_cast<lldb::LanguageType>(lang)); + } + }); + std::lock_guard<std::mutex> guard(GetLanguagesMutex()); LanguagesMap &map(GetLanguagesMap()); for (const auto &entry : map) { @@ -353,11 +385,10 @@ bool Language::ImageListTypeScavenger::Find_Impl( Target *target = exe_scope->CalculateTarget().get(); if (target) { const auto &images(target->GetImages()); - SymbolContext null_sc; ConstString cs_key(key); llvm::DenseSet<SymbolFile *> searched_sym_files; TypeList matches; - images.FindTypes(null_sc, cs_key, false, UINT32_MAX, searched_sym_files, + images.FindTypes(nullptr, cs_key, false, UINT32_MAX, searched_sym_files, matches); for (const auto &match : matches.Types()) { if (match.get()) { diff --git a/source/Target/LanguageRuntime.cpp b/source/Target/LanguageRuntime.cpp index bd02121f6a4d..ea6914fb076d 100644 --- a/source/Target/LanguageRuntime.cpp +++ b/source/Target/LanguageRuntime.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/LanguageRuntime.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/PluginManager.h" @@ -125,11 +121,11 @@ public: return eCallbackReturnStop; } - Searcher::Depth GetDepth() override { + lldb::SearchDepth GetDepth() override { if (SetActualResolver()) return m_actual_resolver_sp->GetDepth(); else - return eDepthTarget; + return lldb::eSearchDepthTarget; } void GetDescription(Stream *s) override { diff --git a/source/Target/Memory.cpp b/source/Target/Memory.cpp index ad1b4093155d..190c5057e881 100644 --- a/source/Target/Memory.cpp +++ b/source/Target/Memory.cpp @@ -8,16 +8,12 @@ //===----------------------------------------------------------------------===// #include "lldb/Target/Memory.h" -// C Includes #include <inttypes.h> -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Core/RangeMap.h" -#include "lldb/Core/State.h" #include "lldb/Target/Process.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Target/MemoryHistory.cpp b/source/Target/MemoryHistory.cpp index 627942796507..eae931482552 100644 --- a/source/Target/MemoryHistory.cpp +++ b/source/Target/MemoryHistory.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/MemoryHistory.h" #include "lldb/Core/PluginManager.h" diff --git a/source/Target/ModuleCache.cpp b/source/Target/ModuleCache.cpp index 19adfbabe277..daf787b1fb22 100644 --- a/source/Target/ModuleCache.cpp +++ b/source/Target/ModuleCache.cpp @@ -76,7 +76,7 @@ FileSpec GetModuleDirectory(const FileSpec &root_dir_spec, const UUID &uuid) { } FileSpec GetSymbolFileSpec(const FileSpec &module_file_spec) { - return FileSpec(module_file_spec.GetPath() + kSymFileExtension, false); + return FileSpec(module_file_spec.GetPath() + kSymFileExtension); } void DeleteExistingModule(const FileSpec &root_dir_spec, @@ -133,7 +133,7 @@ Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, const auto sysroot_module_path_spec = JoinPath(JoinPath(root_dir_spec, hostname), platform_module_spec.GetPath().c_str()); - if (sysroot_module_path_spec.Exists()) { + if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { if (!delete_existing) return Status(); @@ -141,7 +141,7 @@ Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, } const auto error = MakeDirectory( - FileSpec(sysroot_module_path_spec.GetDirectory().AsCString(), false)); + FileSpec(sysroot_module_path_spec.GetDirectory().AsCString())); if (error.Fail()) return error; @@ -159,9 +159,10 @@ ModuleLock::ModuleLock(const FileSpec &root_dir_spec, const UUID &uuid, return; m_file_spec = JoinPath(lock_dir_spec, uuid.GetAsString().c_str()); - m_file.Open(m_file_spec.GetCString(), File::eOpenOptionWrite | - File::eOpenOptionCanCreate | - File::eOpenOptionCloseOnExec); + FileSystem::Instance().Open(m_file, m_file_spec, + File::eOpenOptionWrite | + File::eOpenOptionCanCreate | + File::eOpenOptionCloseOnExec); if (!m_file) { error.SetErrorToErrno(); return; @@ -225,9 +226,10 @@ Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, const auto module_file_path = JoinPath( module_spec_dir, module_spec.GetFileSpec().GetFilename().AsCString()); - if (!module_file_path.Exists()) + if (!FileSystem::Instance().Exists(module_file_path)) return Status("Module %s not found", module_file_path.GetPath().c_str()); - if (module_file_path.GetByteSize() != module_spec.GetObjectSize()) + if (FileSystem::Instance().GetByteSize(module_file_path) != + module_spec.GetObjectSize()) return Status("Module %s has invalid file size", module_file_path.GetPath().c_str()); @@ -252,7 +254,7 @@ Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, return error; FileSpec symfile_spec = GetSymbolFileSpec(cached_module_sp->GetFileSpec()); - if (symfile_spec.Exists()) + if (FileSystem::Instance().Exists(symfile_spec)) cached_module_sp->SetSymbolFileFileSpec(symfile_spec); m_loaded_modules.insert( diff --git a/source/Target/ObjCLanguageRuntime.cpp b/source/Target/ObjCLanguageRuntime.cpp index b1fcee6db63b..8627da938ea3 100644 --- a/source/Target/ObjCLanguageRuntime.cpp +++ b/source/Target/ObjCLanguageRuntime.cpp @@ -108,14 +108,13 @@ ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) { if (!module_sp) return TypeSP(); - const SymbolContext null_sc; const bool exact_match = true; const uint32_t max_matches = UINT32_MAX; TypeList types; llvm::DenseSet<SymbolFile *> searched_symbol_files; const uint32_t num_types = module_sp->FindTypes( - null_sc, name, exact_match, max_matches, searched_symbol_files, types); + name, exact_match, max_matches, searched_symbol_files, types); if (num_types) { uint32_t i; diff --git a/source/Target/OperatingSystem.cpp b/source/Target/OperatingSystem.cpp index 7a1d9d619879..099bfd055ed1 100644 --- a/source/Target/OperatingSystem.cpp +++ b/source/Target/OperatingSystem.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/OperatingSystem.h" #include "lldb/Core/PluginManager.h" #include "lldb/Target/Thread.h" diff --git a/source/Target/PathMappingList.cpp b/source/Target/PathMappingList.cpp index 778728eebb09..c2249cce89b9 100644 --- a/source/Target/PathMappingList.cpp +++ b/source/Target/PathMappingList.cpp @@ -7,19 +7,16 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes #include <climits> #include <cstring> -// Other libraries and framework includes -// Project includes -#include "lldb/lldb-private-enumerations.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/PosixApi.h" #include "lldb/Target/PathMappingList.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" +#include "lldb/lldb-private-enumerations.h" using namespace lldb; using namespace lldb_private; @@ -37,7 +34,7 @@ namespace { ConstString NormalizePath(const ConstString &path) { // If we use "path" to construct a FileSpec, it will normalize the path for // us. We then grab the string and turn it back into a ConstString. - return ConstString(FileSpec(path.GetStringRef(), false).GetPath()); + return ConstString(FileSpec(path.GetStringRef()).GetPath()); } } //---------------------------------------------------------------------- @@ -176,13 +173,13 @@ bool PathMappingList::RemapPath(llvm::StringRef path, // We need to figure out if the "path" argument is relative. If it is, // then we should remap, else skip this entry. if (path_is_relative == eLazyBoolCalculate) { - path_is_relative = FileSpec(path, false).IsRelative() ? eLazyBoolYes : - eLazyBoolNo; + path_is_relative = + FileSpec(path).IsRelative() ? eLazyBoolYes : eLazyBoolNo; } if (!path_is_relative) continue; } - FileSpec remapped(it.second.GetStringRef(), false); + FileSpec remapped(it.second.GetStringRef()); remapped.AppendPathComponent(path); new_path = remapped.GetPath(); return true; @@ -196,7 +193,7 @@ bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) co for (const auto &it : m_pairs) { if (!path_ref.consume_front(it.second.GetStringRef())) continue; - fixed.SetFile(it.first.GetStringRef(), false, FileSpec::Style::native); + fixed.SetFile(it.first.GetStringRef(), FileSpec::Style::native); fixed.AppendPathComponent(path_ref); return true; } @@ -216,10 +213,9 @@ bool PathMappingList::FindFile(const FileSpec &orig_spec, if (orig_path_len >= prefix_len) { if (::strncmp(pos->first.GetCString(), orig_path, prefix_len) == 0) { - new_spec.SetFile(pos->second.GetCString(), false, - FileSpec::Style::native); + new_spec.SetFile(pos->second.GetCString(), FileSpec::Style::native); new_spec.AppendPathComponent(orig_path + prefix_len); - if (new_spec.Exists()) + if (FileSystem::Instance().Exists(new_spec)) return true; } } diff --git a/source/Target/Platform.cpp b/source/Target/Platform.cpp index 5ae556ecc02a..400b3c92b5ab 100644 --- a/source/Target/Platform.cpp +++ b/source/Target/Platform.cpp @@ -7,18 +7,14 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes #include <algorithm> #include <csignal> #include <fstream> #include <vector> -// Other libraries and framework includes #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" -// Project includes #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" @@ -67,12 +63,11 @@ const char *Platform::GetHostPlatformName() { return "host"; } namespace { -PropertyDefinition g_properties[] = { +static constexpr PropertyDefinition g_properties[] = { {"use-module-cache", OptionValue::eTypeBoolean, true, true, nullptr, - nullptr, "Use module cache."}, + {}, "Use module cache."}, {"module-cache-directory", OptionValue::eTypeFileSpec, true, 0, nullptr, - nullptr, "Root directory for cached modules."}, - {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; + {}, "Root directory for cached modules."}}; enum { ePropertyUseModuleCache, ePropertyModuleCacheDirectory }; @@ -95,7 +90,7 @@ PlatformProperties::PlatformProperties() { if (!llvm::sys::path::home_directory(user_home_dir)) return; - module_cache_dir = FileSpec(user_home_dir.c_str(), false); + module_cache_dir = FileSpec(user_home_dir.c_str()); module_cache_dir.AppendPathComponent(".lldb"); module_cache_dir.AppendPathComponent("module_cache"); SetModuleCacheDirectory(module_cache_dir); @@ -228,16 +223,35 @@ Status Platform::GetSharedModule(const ModuleSpec &module_spec, module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false); - return GetRemoteSharedModule(module_spec, process, module_sp, - [&](const ModuleSpec &spec) { - Status error = ModuleList::GetSharedModule( - spec, module_sp, module_search_paths_ptr, - old_module_sp_ptr, did_create_ptr, false); - if (error.Success() && module_sp) - module_sp->SetPlatformFileSpec( - spec.GetFileSpec()); - return error; - }, + // Module resolver lambda. + auto resolver = [&](const ModuleSpec &spec) { + Status error(eErrorTypeGeneric); + ModuleSpec resolved_spec; + // Check if we have sysroot set. + if (m_sdk_sysroot) { + // Prepend sysroot to module spec. + resolved_spec = spec; + resolved_spec.GetFileSpec().PrependPathComponent( + m_sdk_sysroot.GetStringRef()); + // Try to get shared module with resolved spec. + error = ModuleList::GetSharedModule( + resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, + did_create_ptr, false); + } + // If we don't have sysroot or it didn't work then + // try original module spec. + if (!error.Success()) { + resolved_spec = spec; + error = ModuleList::GetSharedModule( + resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, + did_create_ptr, false); + } + if (error.Success() && module_sp) + module_sp->SetPlatformFileSpec(resolved_spec.GetFileSpec()); + return error; + }; + + return GetRemoteSharedModule(module_spec, process, module_sp, resolver, did_create_ptr); } @@ -518,9 +532,12 @@ FileSpec Platform::GetWorkingDirectory() { if (IsHost()) { llvm::SmallString<64> cwd; if (llvm::sys::fs::current_path(cwd)) - return FileSpec{}; - else - return FileSpec(cwd, true); + return {}; + else { + FileSpec file_spec(cwd); + FileSystem::Instance().Resolve(file_spec); + return file_spec; + } } else { if (!m_working_dir) m_working_dir = GetRemoteWorkingDirectory(); @@ -534,16 +551,17 @@ struct RecurseCopyBaton { Status error; }; -static FileSpec::EnumerateDirectoryResult +static FileSystem::EnumerateDirectoryResult RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, - const FileSpec &src) { + llvm::StringRef path) { RecurseCopyBaton *rc_baton = (RecurseCopyBaton *)baton; + FileSpec src(path); namespace fs = llvm::sys::fs; switch (ft) { case fs::file_type::fifo_file: case fs::file_type::socket_file: // we have no way to copy pipes and sockets - ignore them and continue - return FileSpec::eEnumerateDirectoryResultNext; + return FileSystem::eEnumerateDirectoryResultNext; break; case fs::file_type::directory_file: { @@ -556,7 +574,7 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, if (error.Fail()) { rc_baton->error.SetErrorStringWithFormat( "unable to setup directory %s on remote end", dst_dir.GetCString()); - return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } // now recurse @@ -568,13 +586,13 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str()); RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr, Status()}; - FileSpec::EnumerateDirectory(src_dir_path, true, true, true, - RecurseCopy_Callback, &rc_baton2); + FileSystem::Instance().EnumerateDirectory(src_dir_path, true, true, true, + RecurseCopy_Callback, &rc_baton2); if (rc_baton2.error.Fail()) { rc_baton->error.SetErrorString(rc_baton2.error.AsCString()); - return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } - return FileSpec::eEnumerateDirectoryResultNext; + return FileSystem::eEnumerateDirectoryResultNext; } break; case fs::file_type::symlink_file: { @@ -585,18 +603,18 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, FileSpec src_resolved; - rc_baton->error = FileSystem::Readlink(src, src_resolved); + rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved); if (rc_baton->error.Fail()) - return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out rc_baton->error = rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved); if (rc_baton->error.Fail()) - return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out - return FileSpec::eEnumerateDirectoryResultNext; + return FileSystem::eEnumerateDirectoryResultNext; } break; case fs::file_type::regular_file: { @@ -607,15 +625,15 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, Status err = rc_baton->platform_ptr->PutFile(src, dst_file); if (err.Fail()) { rc_baton->error.SetErrorString(err.AsCString()); - return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } - return FileSpec::eEnumerateDirectoryResultNext; + return FileSystem::eEnumerateDirectoryResultNext; } break; default: rc_baton->error.SetErrorStringWithFormat( "invalid file detected during copy: %s", src.GetPath().c_str()); - return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out break; } llvm_unreachable("Unhandled file_type!"); @@ -690,7 +708,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { switch (fs::get_file_type(src.GetPath(), false)) { case fs::file_type::directory_file: { llvm::sys::fs::remove(fixed_dst.GetPath()); - uint32_t permissions = src.GetPermissions(); + uint32_t permissions = FileSystem::Instance().GetPermissions(src); if (permissions == 0) permissions = eFilePermissionsDirectoryDefault; error = MakeDirectory(fixed_dst, permissions); @@ -701,8 +719,8 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString()); std::string src_dir_path(src.GetPath()); RecurseCopyBaton baton = {recurse_dst, this, Status()}; - FileSpec::EnumerateDirectory(src_dir_path, true, true, true, - RecurseCopy_Callback, &baton); + FileSystem::Instance().EnumerateDirectory( + src_dir_path, true, true, true, RecurseCopy_Callback, &baton); return baton.error; } } break; @@ -715,7 +733,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { case fs::file_type::symlink_file: { llvm::sys::fs::remove(fixed_dst.GetPath()); FileSpec src_resolved; - error = FileSystem::Readlink(src, src_resolved); + error = FileSystem::Instance().Readlink(src, src_resolved); if (error.Success()) error = CreateSymlink(dst, src_resolved); } break; @@ -871,7 +889,7 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Status error; - if (module_spec.GetFileSpec().Exists()) { + if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { if (module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule(module_spec, exe_module_sp, module_search_paths_ptr, nullptr, @@ -902,7 +920,7 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, Status Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec, FileSpec &sym_file) { Status error; - if (sym_spec.GetSymbolFileSpec().Exists()) + if (FileSystem::Instance().Exists(sym_spec.GetSymbolFileSpec())) sym_file = sym_spec.GetSymbolFileSpec(); else error.SetErrorString("unable to resolve symbol file"); @@ -912,7 +930,8 @@ Status Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec, bool Platform::ResolveRemotePath(const FileSpec &platform_path, FileSpec &resolved_platform_path) { resolved_platform_path = platform_path; - return resolved_platform_path.ResolvePath(); + FileSystem::Instance().Resolve(resolved_platform_path); + return true; } const ArchSpec &Platform::GetSystemArchitecture() { @@ -1257,8 +1276,9 @@ Status Platform::PutFile(const FileSpec &source, const FileSpec &destination, if (fs::is_symlink_file(source.GetPath())) source_open_options |= File::eOpenOptionDontFollowSymlinks; - File source_file(source, source_open_options, lldb::eFilePermissionsUserRW); - Status error; + File source_file; + Status error = FileSystem::Instance().Open( + source_file, source, source_open_options, lldb::eFilePermissionsUserRW); uint32_t permissions = source_file.GetPermissions(error); if (permissions == 0) permissions = lldb::eFilePermissionsFileDefault; @@ -1276,7 +1296,7 @@ Status Platform::PutFile(const FileSpec &source, const FileSpec &destination, return error; if (dest_file == UINT64_MAX) return Status("unable to open target file"); - lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); + lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024 * 16, 0)); uint64_t offset = 0; for (;;) { size_t bytes_read = buffer_sp->GetByteSize(); @@ -1378,32 +1398,32 @@ const char *Platform::GetLocalCacheDirectory() { return m_local_cache_directory.c_str(); } -static OptionDefinition g_rsync_option_table[] = { +static constexpr OptionDefinition g_rsync_option_table[] = { {LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr, - nullptr, 0, eArgTypeNone, "Enable rsync."}, + {}, 0, eArgTypeNone, "Enable rsync."}, {LLDB_OPT_SET_ALL, false, "rsync-opts", 'R', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommandName, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, "Platform-specific options required for rsync to work."}, {LLDB_OPT_SET_ALL, false, "rsync-prefix", 'P', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommandName, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName, "Platform-specific rsync prefix put before the remote path."}, {LLDB_OPT_SET_ALL, false, "ignore-remote-hostname", 'i', - OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Do not automatically fill in the remote hostname when composing the " "rsync command."}, }; -static OptionDefinition g_ssh_option_table[] = { +static constexpr OptionDefinition g_ssh_option_table[] = { {LLDB_OPT_SET_ALL, false, "ssh", 's', OptionParser::eNoArgument, nullptr, - nullptr, 0, eArgTypeNone, "Enable SSH."}, + {}, 0, eArgTypeNone, "Enable SSH."}, {LLDB_OPT_SET_ALL, false, "ssh-opts", 'S', OptionParser::eRequiredArgument, - nullptr, nullptr, 0, eArgTypeCommandName, + nullptr, {}, 0, eArgTypeCommandName, "Platform-specific options required for SSH to work."}, }; -static OptionDefinition g_caching_option_table[] = { +static constexpr OptionDefinition g_caching_option_table[] = { {LLDB_OPT_SET_ALL, false, "local-cache-dir", 'c', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePath, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePath, "Path in which to store local copies of files."}, }; @@ -1566,14 +1586,14 @@ Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, if (process->GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), resolved_module_spec)) { - if (module_spec.GetUUID().IsValid() == false || + if (!module_spec.GetUUID().IsValid() || module_spec.GetUUID() == resolved_module_spec.GetUUID()) { got_module_spec = true; } } } - if (module_spec.GetArchitecture().IsValid() == false) { + if (!module_spec.GetArchitecture().IsValid()) { Status error; // No valid architecture was specified, ask the platform for the // architectures that we should be using (in the correct order) and see if @@ -1596,7 +1616,7 @@ Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, // Get module information from a target. if (!GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), resolved_module_spec)) { - if (module_spec.GetUUID().IsValid() == false || + if (!module_spec.GetUUID().IsValid() || module_spec.GetUUID() == resolved_module_spec.GetUUID()) { return module_resolver(module_spec); } @@ -1780,9 +1800,9 @@ uint32_t Platform::LoadImageUsingPaths(lldb_private::Process *process, { FileSpec file_to_use; if (remote_filename.IsAbsolute()) - file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(), - false, - remote_filename.GetPathStyle()); + file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(), + + remote_filename.GetPathStyle()); else file_to_use = remote_filename; @@ -1802,9 +1822,19 @@ lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url, error.Clear(); if (!target) { + ArchSpec arch; + if (target && target->GetArchitecture().IsValid()) + arch = target->GetArchitecture(); + else + arch = Target::GetDefaultArchitecture(); + + const char *triple = ""; + if (arch.IsValid()) + triple = arch.GetTriple().getTriple().c_str(); + TargetSP new_target_sp; - error = debugger.GetTargetList().CreateTarget(debugger, "", "", false, - nullptr, new_target_sp); + error = debugger.GetTargetList().CreateTarget( + debugger, "", triple, eLoadDependentsNo, nullptr, new_target_sp); target = new_target_sp.get(); } diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index c3d8abc9f78d..fb3b758912eb 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -7,25 +7,19 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes #include <atomic> #include <mutex> -// Other libraries and framework includes #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Threading.h" -// Project includes #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/Event.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" -#include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/IRDynamicChecks.h" @@ -67,9 +61,11 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Event.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/State.h" using namespace lldb; using namespace lldb_private; @@ -116,39 +112,38 @@ public: } }; -static PropertyDefinition g_properties[] = { +static constexpr PropertyDefinition g_properties[] = { {"disable-memory-cache", OptionValue::eTypeBoolean, false, - DISABLE_MEM_CACHE_DEFAULT, nullptr, nullptr, + DISABLE_MEM_CACHE_DEFAULT, nullptr, {}, "Disable reading and caching of memory in fixed-size units."}, {"extra-startup-command", OptionValue::eTypeArray, false, - OptionValue::eTypeString, nullptr, nullptr, + OptionValue::eTypeString, nullptr, {}, "A list containing extra commands understood by the particular process " "plugin used. " "For instance, to turn on debugserver logging set this to " "\"QSetLogging:bitmask=LOG_DEFAULT;\""}, {"ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, true, - nullptr, nullptr, + nullptr, {}, "If true, breakpoints will be ignored during expression evaluation."}, {"unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true, - nullptr, nullptr, "If true, errors in expression evaluation will unwind " - "the stack back to the state before the call."}, + nullptr, {}, "If true, errors in expression evaluation will unwind " + "the stack back to the state before the call."}, {"python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, nullptr, - nullptr, "A path to a python OS plug-in module file that contains a " - "OperatingSystemPlugIn class."}, + {}, "A path to a python OS plug-in module file that contains a " + "OperatingSystemPlugIn class."}, {"stop-on-sharedlibrary-events", OptionValue::eTypeBoolean, true, false, - nullptr, nullptr, + nullptr, {}, "If true, stop when a shared library is loaded or unloaded."}, {"detach-keeps-stopped", OptionValue::eTypeBoolean, true, false, nullptr, - nullptr, "If true, detach will attempt to keep the process stopped."}, + {}, "If true, detach will attempt to keep the process stopped."}, {"memory-cache-line-size", OptionValue::eTypeUInt64, false, 512, nullptr, - nullptr, "The memory cache line size"}, + {}, "The memory cache line size"}, {"optimization-warnings", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "If true, warn when stopped in code that is optimized where " - "stepping and variable availability may not behave as expected."}, + {}, "If true, warn when stopped in code that is optimized where " + "stepping and variable availability may not behave as expected."}, {"stop-on-exec", OptionValue::eTypeBoolean, true, true, - nullptr, nullptr, - "If true, stop when a shared library is loaded or unloaded."}, - {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; + nullptr, {}, + "If true, stop when a shared library is loaded or unloaded."}}; enum { ePropertyDisableMemCache, @@ -431,7 +426,7 @@ Status ProcessLaunchCommandOptions::SetOptionValue( case 'i': // STDIN for read only { FileAction action; - if (action.Open(STDIN_FILENO, FileSpec{option_arg, false}, true, false)) + if (action.Open(STDIN_FILENO, FileSpec(option_arg), true, false)) launch_info.AppendFileAction(action); break; } @@ -439,7 +434,7 @@ Status ProcessLaunchCommandOptions::SetOptionValue( case 'o': // Open STDOUT for write only { FileAction action; - if (action.Open(STDOUT_FILENO, FileSpec{option_arg, false}, false, true)) + if (action.Open(STDOUT_FILENO, FileSpec(option_arg), false, true)) launch_info.AppendFileAction(action); break; } @@ -447,7 +442,7 @@ Status ProcessLaunchCommandOptions::SetOptionValue( case 'e': // STDERR for write only { FileAction action; - if (action.Open(STDERR_FILENO, FileSpec{option_arg, false}, false, true)) + if (action.Open(STDERR_FILENO, FileSpec(option_arg), false, true)) launch_info.AppendFileAction(action); break; } @@ -459,7 +454,7 @@ Status ProcessLaunchCommandOptions::SetOptionValue( case 'n': // Disable STDIO { FileAction action; - const FileSpec dev_null{FileSystem::DEV_NULL, false}; + const FileSpec dev_null(FileSystem::DEV_NULL); if (action.Open(STDIN_FILENO, dev_null, true, false)) launch_info.AppendFileAction(action); if (action.Open(STDOUT_FILENO, dev_null, false, true)) @@ -470,7 +465,7 @@ Status ProcessLaunchCommandOptions::SetOptionValue( } case 'w': - launch_info.SetWorkingDirectory(FileSpec{option_arg, false}); + launch_info.SetWorkingDirectory(FileSpec(option_arg)); break; case 't': // Open process in new terminal window @@ -516,7 +511,7 @@ Status ProcessLaunchCommandOptions::SetOptionValue( case 'c': if (!option_arg.empty()) - launch_info.SetShell(FileSpec(option_arg, false)); + launch_info.SetShell(FileSpec(option_arg)); else launch_info.SetShell(HostInfo::GetDefaultShell()); break; @@ -533,52 +528,52 @@ Status ProcessLaunchCommandOptions::SetOptionValue( return error; } -static OptionDefinition g_process_launch_options[] = { +static constexpr OptionDefinition g_process_launch_options[] = { {LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument, - nullptr, nullptr, 0, eArgTypeNone, + nullptr, {}, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."}, {LLDB_OPT_SET_ALL, false, "disable-aslr", 'A', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Set whether to disable address space layout randomization when launching " "a process."}, {LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, - nullptr, nullptr, 0, eArgTypePlugin, + nullptr, {}, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, {LLDB_OPT_SET_ALL, false, "working-dir", 'w', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeDirectoryName, "Set the current working directory to <path> when running the inferior."}, {LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument, - nullptr, nullptr, 0, eArgTypeArchitecture, + nullptr, {}, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."}, {LLDB_OPT_SET_ALL, false, "environment", 'v', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "Specify an environment variable name/value string (--environment " "NAME=VALUE). Can be specified multiple times for subsequent environment " "entries."}, {LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "shell", 'c', - OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeFilename, + OptionParser::eOptionalArgument, nullptr, {}, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."}, {LLDB_OPT_SET_1, false, "stdin", 'i', OptionParser::eRequiredArgument, - nullptr, nullptr, 0, eArgTypeFilename, + nullptr, {}, 0, eArgTypeFilename, "Redirect stdin for the process to <filename>."}, {LLDB_OPT_SET_1, false, "stdout", 'o', OptionParser::eRequiredArgument, - nullptr, nullptr, 0, eArgTypeFilename, + nullptr, {}, 0, eArgTypeFilename, "Redirect stdout for the process to <filename>."}, {LLDB_OPT_SET_1, false, "stderr", 'e', OptionParser::eRequiredArgument, - nullptr, nullptr, 0, eArgTypeFilename, + nullptr, {}, 0, eArgTypeFilename, "Redirect stderr for the process to <filename>."}, {LLDB_OPT_SET_2, false, "tty", 't', OptionParser::eNoArgument, nullptr, - nullptr, 0, eArgTypeNone, + {}, 0, eArgTypeNone, "Start the process in a terminal (not supported on all platforms)."}, {LLDB_OPT_SET_3, false, "no-stdio", 'n', OptionParser::eNoArgument, nullptr, - nullptr, 0, eArgTypeNone, + {}, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, {LLDB_OPT_SET_4, false, "shell-expand-args", 'X', - OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, + OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeBoolean, "Set whether to shell expand arguments to the process when launching."}, }; @@ -1729,6 +1724,10 @@ void Process::SetRunningUserExpression(bool on) { m_mod_id.SetRunningUserExpression(on); } +void Process::SetRunningUtilityFunction(bool on) { + m_mod_id.SetRunningUtilityFunction(on); +} + addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; } const lldb::ABISP &Process::GetABI() { @@ -1921,7 +1920,7 @@ Process::CreateBreakpointSite(const BreakpointLocationSP &owner, owner->SetBreakpointSite(bp_site_sp); return m_breakpoint_site_list.Add(bp_site_sp); } else { - if (show_error) { + if (show_error || use_hardware) { // Report error for setting breakpoint... GetTarget().GetDebugger().GetErrorFile()->Printf( "warning: failed to set breakpoint site at 0x%" PRIx64 @@ -2731,7 +2730,7 @@ Status Process::Launch(ProcessLaunchInfo &launch_info) { sizeof(local_exec_file_path)); exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, sizeof(platform_exec_file_path)); - if (exe_module->GetFileSpec().Exists()) { + if (FileSystem::Instance().Exists(exe_module->GetFileSpec())) { // Install anything that might need to be installed prior to launching. // For host systems, this will do nothing, but if we are connected to a // remote platform it will install any needed binaries @@ -3211,7 +3210,8 @@ void Process::CompleteAttach() { } } if (new_executable_module_sp) { - GetTarget().SetExecutableModule(new_executable_module_sp, false); + GetTarget().SetExecutableModule(new_executable_module_sp, + eLoadDependentsNo); if (log) { ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); log->Printf( @@ -3291,6 +3291,11 @@ Status Process::PrivateResume() { m_thread_list.DidResume(); if (log) log->Printf("Process thinks the process has resumed."); + } else { + if (log) + log->Printf( + "Process::PrivateResume() DoResume failed."); + return error; } } } else { @@ -4685,7 +4690,12 @@ bool Process::PushProcessIOHandler() { log->Printf("Process::%s pushing IO handler", __FUNCTION__); io_handler_sp->SetIsDone(false); - GetTarget().GetDebugger().PushIOHandler(io_handler_sp); + // If we evaluate an utility function, then we don't cancel the current + // IOHandler. Our IOHandler is non-interactive and shouldn't disturb the + // existing IOHandler that potentially provides the user interface (e.g. + // the IOHandler for Editline). + bool cancel_top_handler = !m_mod_id.IsRunningUtilityFunction(); + GetTarget().GetDebugger().PushIOHandler(io_handler_sp, cancel_top_handler); return true; } return false; @@ -4875,6 +4885,11 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx, thread_plan_sp->SetIsMasterPlan(true); thread_plan_sp->SetOkayToDiscard(false); + // If we are running some utility expression for LLDB, we now have to mark + // this in the ProcesModID of this process. This RAII takes care of marking + // and reverting the mark it once we are done running the expression. + UtilityFunctionScope util_scope(options.IsForUtilityExpr() ? this : nullptr); + if (m_private_state.GetValue() != eStateStopped) { diagnostic_manager.PutString( eDiagnosticSeverityError, @@ -5842,7 +5857,7 @@ void Process::ModulesDidLoad(ModuleList &module_list) { // that loaded. // Iterate over a copy of this language runtime list in case the language - // runtime ModulesDidLoad somehow causes the language riuntime to be + // runtime ModulesDidLoad somehow causes the language runtime to be // unloaded. LanguageRuntimeCollection language_runtimes(m_language_runtimes); for (const auto &pair : language_runtimes) { @@ -6020,7 +6035,7 @@ Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr, } Status -Process::GetMemoryRegions(std::vector<lldb::MemoryRegionInfoSP> ®ion_list) { +Process::GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) { Status error; @@ -6028,17 +6043,17 @@ Process::GetMemoryRegions(std::vector<lldb::MemoryRegionInfoSP> ®ion_list) { region_list.clear(); do { - lldb::MemoryRegionInfoSP region_info(new lldb_private::MemoryRegionInfo()); - error = GetMemoryRegionInfo(range_end, *region_info); + lldb_private::MemoryRegionInfo region_info; + error = GetMemoryRegionInfo(range_end, region_info); // GetMemoryRegionInfo should only return an error if it is unimplemented. if (error.Fail()) { region_list.clear(); break; } - range_end = region_info->GetRange().GetRangeEnd(); - if (region_info->GetMapped() == MemoryRegionInfo::eYes) { - region_list.push_back(region_info); + range_end = region_info.GetRange().GetRangeEnd(); + if (region_info.GetMapped() == MemoryRegionInfo::eYes) { + region_list.push_back(std::move(region_info)); } } while (range_end != LLDB_INVALID_ADDRESS); @@ -6095,7 +6110,7 @@ void Process::MapSupportedStructuredDataPlugins( // For each StructuredDataPlugin, if the plugin handles any of the types in // the supported_type_names, map that type name to that plugin. Stop when // we've consumed all the type names. - // FIXME: should we return an error if there are type names nobody + // FIXME: should we return an error if there are type names nobody // supports? for (uint32_t plugin_index = 0; !const_type_names.empty(); plugin_index++) { auto create_instance = @@ -6103,7 +6118,7 @@ void Process::MapSupportedStructuredDataPlugins( plugin_index); if (!create_instance) break; - + // Create the plugin. StructuredDataPluginSP plugin_sp = (*create_instance)(*this); if (!plugin_sp) { diff --git a/source/Target/ProcessInfo.cpp b/source/Target/ProcessInfo.cpp index ac0350686706..1ada6123fa1e 100644 --- a/source/Target/ProcessInfo.cpp +++ b/source/Target/ProcessInfo.cpp @@ -9,12 +9,8 @@ #include "lldb/Target/ProcessInfo.h" -// C Includes -// C++ Includes #include <climits> -// Other libraries and framework includes -// Project includes #include "lldb/Host/PosixApi.h" #include "lldb/Utility/Stream.h" @@ -29,8 +25,8 @@ ProcessInfo::ProcessInfo() ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch, lldb::pid_t pid) - : m_executable(name, false), m_arguments(), m_environment(), - m_uid(UINT32_MAX), m_gid(UINT32_MAX), m_arch(arch), m_pid(pid) {} + : m_executable(name), m_arguments(), m_environment(), m_uid(UINT32_MAX), + m_gid(UINT32_MAX), m_arch(arch), m_pid(pid) {} void ProcessInfo::Clear() { m_executable.Clear(); @@ -67,7 +63,7 @@ void ProcessInfo::SetExecutableFile(const FileSpec &exe_file, if (exe_file) { m_executable = exe_file; if (add_exe_file_as_first_arg) { - llvm::SmallString<PATH_MAX> filename; + llvm::SmallString<128> filename; exe_file.GetPath(filename); if (!filename.empty()) m_arguments.InsertArgumentAtIndex(0, filename); @@ -96,8 +92,7 @@ void ProcessInfo::SetArguments(char const **argv, // Yes the first argument is an executable, set it as the executable in // the launch options. Don't resolve the file path as the path could be a // remote platform path - const bool resolve = false; - m_executable.SetFile(first_arg, resolve, FileSpec::Style::native); + m_executable.SetFile(first_arg, FileSpec::Style::native); } } } @@ -113,8 +108,7 @@ void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) { // Yes the first argument is an executable, set it as the executable in // the launch options. Don't resolve the file path as the path could be a // remote platform path - const bool resolve = false; - m_executable.SetFile(first_arg, resolve, FileSpec::Style::native); + m_executable.SetFile(first_arg, FileSpec::Style::native); } } } diff --git a/source/Target/ProcessLaunchInfo.cpp b/source/Target/ProcessLaunchInfo.cpp index 9569750bc5fd..ac1eba04f0cb 100644 --- a/source/Target/ProcessLaunchInfo.cpp +++ b/source/Target/ProcessLaunchInfo.cpp @@ -7,19 +7,13 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes #include <climits> -// Other libraries and framework includes -// Project includes -#include "lldb/Core/Debugger.h" #include "lldb/Host/Config.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/ProcessLaunchInfo.h" -#include "lldb/Target/Target.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" @@ -108,8 +102,7 @@ bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec, bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read, bool write) { FileAction file_action; - if (file_action.Open(fd, FileSpec{FileSystem::DEV_NULL, false}, read, - write)) { + if (file_action.Open(fd, FileSpec(FileSystem::DEV_NULL), read, write)) { AppendFileAction(file_action); return true; } @@ -151,7 +144,7 @@ const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; } void ProcessLaunchInfo::SetShell(const FileSpec &shell) { m_shell = shell; if (m_shell) { - m_shell.ResolveExecutableLocation(); + FileSystem::Instance().ResolveExecutableLocation(m_shell); m_flags.Set(lldb::eLaunchFlagLaunchInShell); } else m_flags.Clear(lldb::eLaunchFlagLaunchInShell); @@ -212,124 +205,38 @@ void ProcessLaunchInfo::SetDetachOnError(bool enable) { m_flags.Clear(lldb::eLaunchFlagDetachOnError); } -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) == nullptr || - GetFileActionForFD(STDOUT_FILENO) == nullptr || - GetFileActionForFD(STDERR_FILENO) == nullptr) { - if (log) - log->Printf("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr " - "was not set, evaluating default handling", - __FUNCTION__); - - if (m_flags.Test(eLaunchFlagLaunchInTTY)) { - // Do nothing, if we are launching in a remote terminal no file actions - // should be done at all. - return; - } - - 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); - } else { - // Check for any values that might have gotten set with any of: (lldb) - // settings set target.input-path (lldb) settings set target.output-path - // (lldb) settings set target.error-path - FileSpec in_file_spec; - FileSpec out_file_spec; - FileSpec err_file_spec; - if (target) { - // Only override with the target settings if we don't already have an - // action for in, out or error - if (GetFileActionForFD(STDIN_FILENO) == nullptr) - in_file_spec = target->GetStandardInputPath(); - if (GetFileActionForFD(STDOUT_FILENO) == nullptr) - out_file_spec = target->GetStandardOutputPath(); - if (GetFileActionForFD(STDERR_FILENO) == nullptr) - err_file_spec = target->GetStandardErrorPath(); - } - - if (log) - log->Printf("ProcessLaunchInfo::%s target stdin='%s', target " - "stdout='%s', stderr='%s'", - __FUNCTION__, - in_file_spec ? in_file_spec.GetCString() : "<null>", - out_file_spec ? out_file_spec.GetCString() : "<null>", - err_file_spec ? err_file_spec.GetCString() : "<null>"); - - if (in_file_spec) { - AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false); - if (log) - log->Printf( - "ProcessLaunchInfo::%s appended stdin open file action for %s", - __FUNCTION__, in_file_spec.GetCString()); - } - - if (out_file_spec) { - AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true); - if (log) - log->Printf( - "ProcessLaunchInfo::%s appended stdout open file action for %s", - __FUNCTION__, out_file_spec.GetCString()); - } - - if (err_file_spec) { - AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true); - if (log) - log->Printf( - "ProcessLaunchInfo::%s appended stderr open file action for %s", - __FUNCTION__, err_file_spec.GetCString()); - } - - if (default_to_use_pty && - (!in_file_spec || !out_file_spec || !err_file_spec)) { - 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__); +llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); + LLDB_LOG(log, "Generating a pty to use for stdin/out/err"); - int open_flags = O_RDWR | O_NOCTTY; + int open_flags = O_RDWR | O_NOCTTY; #if !defined(_WIN32) - // We really shouldn't be specifying platform specific flags that are - // intended for a system call in generic code. But this will have to - // do for now. - open_flags |= O_CLOEXEC; + // We really shouldn't be specifying platform specific flags that are + // intended for a system call in generic code. But this will have to + // do for now. + open_flags |= O_CLOEXEC; #endif - if (m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0)) { - const FileSpec slave_file_spec{m_pty->GetSlaveName(nullptr, 0), - false}; - - // Only use the slave tty if we don't have anything specified for - // input and don't have an action for stdin - if (!in_file_spec && GetFileActionForFD(STDIN_FILENO) == nullptr) { - AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false); - } - - // Only use the slave tty if we don't have anything specified for - // output and don't have an action for stdout - if (!out_file_spec && GetFileActionForFD(STDOUT_FILENO) == nullptr) { - AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true); - } - - // Only use the slave tty if we don't have anything specified for - // error and don't have an action for stderr - if (!err_file_spec && GetFileActionForFD(STDERR_FILENO) == nullptr) { - AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true); - } - } - } - } + if (!m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0)) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "PTY::OpenFirstAvailableMaster failed"); } + const FileSpec slave_file_spec(m_pty->GetSlaveName(nullptr, 0)); + + // Only use the slave tty if we don't have anything specified for + // input and don't have an action for stdin + if (GetFileActionForFD(STDIN_FILENO) == nullptr) + AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false); + + // Only use the slave tty if we don't have anything specified for + // output and don't have an action for stdout + if (GetFileActionForFD(STDOUT_FILENO) == nullptr) + AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true); + + // Only use the slave tty if we don't have anything specified for + // error and don't have an action for stderr + if (GetFileActionForFD(STDERR_FILENO) == nullptr) + AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true); + return llvm::Error::success(); } bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell( @@ -359,7 +266,7 @@ bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell( // Add a modified PATH environment variable in case argv[0] is a // relative path. const char *argv0 = argv[0]; - FileSpec arg_spec(argv0, false); + FileSpec arg_spec(argv0); if (arg_spec.IsRelative()) { // 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") @@ -439,10 +346,3 @@ bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell( } return false; } - -ListenerSP ProcessLaunchInfo::GetListenerForProcess(Debugger &debugger) { - if (m_listener_sp) - return m_listener_sp; - else - return debugger.GetListener(); -} diff --git a/source/Target/Queue.cpp b/source/Target/Queue.cpp index 45fdbea13db5..c0683d10b151 100644 --- a/source/Target/Queue.cpp +++ b/source/Target/Queue.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/Queue.h" #include "lldb/Target/Process.h" #include "lldb/Target/QueueList.h" diff --git a/source/Target/QueueItem.cpp b/source/Target/QueueItem.cpp index fe58980c01c1..a20fa918a758 100644 --- a/source/Target/QueueItem.cpp +++ b/source/Target/QueueItem.cpp @@ -94,7 +94,7 @@ std::string QueueItem::GetQueueLabel() { ProcessSP QueueItem::GetProcessSP() { return m_process_wp.lock(); } void QueueItem::FetchEntireItem() { - if (m_have_fetched_entire_item == true) + if (m_have_fetched_entire_item) return; ProcessSP process_sp = m_process_wp.lock(); if (process_sp) { diff --git a/source/Target/RegisterContext.cpp b/source/Target/RegisterContext.cpp index eaec03d9b595..976f9e8b45e7 100644 --- a/source/Target/RegisterContext.cpp +++ b/source/Target/RegisterContext.cpp @@ -7,14 +7,8 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/RegisterContext.h" #include "lldb/Core/Module.h" -#include "lldb/Core/RegisterValue.h" -#include "lldb/Core/Scalar.h" #include "lldb/Core/Value.h" #include "lldb/Expression/DWARFExpression.h" #include "lldb/Target/ExecutionContext.h" @@ -24,6 +18,8 @@ #include "lldb/Target/Thread.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Target/RegisterNumber.cpp b/source/Target/RegisterNumber.cpp index d1bb8adf56e1..8b01bd103395 100644 --- a/source/Target/RegisterNumber.cpp +++ b/source/Target/RegisterNumber.cpp @@ -61,26 +61,15 @@ bool RegisterNumber::operator==(RegisterNumber &rhs) { return false; if (m_kind == rhs.m_kind) { - if (m_regnum == rhs.m_regnum) - return true; - else - return false; + return m_regnum == rhs.m_regnum; } uint32_t rhs_regnum = rhs.GetAsKind(m_kind); if (rhs_regnum != LLDB_INVALID_REGNUM) { - if (m_regnum == rhs_regnum) - return true; - else - return false; + return m_regnum == rhs_regnum; } uint32_t lhs_regnum = GetAsKind(rhs.m_kind); - { - if (lhs_regnum == rhs.m_regnum) - return true; - else - return false; - } + { return lhs_regnum == rhs.m_regnum; } return false; } diff --git a/source/Target/SectionLoadHistory.cpp b/source/Target/SectionLoadHistory.cpp index 5844da5d41e9..1229933793ff 100644 --- a/source/Target/SectionLoadHistory.cpp +++ b/source/Target/SectionLoadHistory.cpp @@ -9,10 +9,6 @@ #include "lldb/Target/SectionLoadHistory.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/SectionLoadList.h" #include "lldb/Utility/Stream.h" diff --git a/source/Target/SectionLoadList.cpp b/source/Target/SectionLoadList.cpp index 6839aaccaa52..ea9f7ea9911c 100644 --- a/source/Target/SectionLoadList.cpp +++ b/source/Target/SectionLoadList.cpp @@ -9,10 +9,6 @@ #include "lldb/Target/SectionLoadList.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/Block.h" diff --git a/source/Target/StackFrame.cpp b/source/Target/StackFrame.cpp index 2b9260f95f3f..3cea6444596c 100644 --- a/source/Target/StackFrame.cpp +++ b/source/Target/StackFrame.cpp @@ -7,17 +7,12 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/StackFrame.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" -#include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectMemory.h" @@ -32,8 +27,12 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" + +#include "lldb/lldb-enumerations.h" using namespace lldb; using namespace lldb_private; @@ -49,20 +48,19 @@ using namespace lldb_private; StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, addr_t cfa, - bool cfa_is_valid, addr_t pc, uint32_t stop_id, - bool stop_id_is_valid, bool is_history_frame, + bool cfa_is_valid, addr_t pc, StackFrame::Kind kind, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), - m_stop_id(stop_id), m_stop_id_is_valid(stop_id_is_valid), - m_is_history_frame(is_history_frame), m_variable_list_sp(), - m_variable_list_value_objects(), m_disassembly(), m_mutex() { + m_stack_frame_kind(kind), m_variable_list_sp(), + m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), + m_mutex() { // 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. - if (m_is_history_frame && !m_cfa_is_valid) { + if (IsHistorical() && !m_cfa_is_valid) { m_id.SetCFA(m_frame_index); } @@ -80,9 +78,9 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), - m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0), - m_stop_id_is_valid(false), m_is_history_frame(false), - m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), + m_frame_base_error(), m_cfa_is_valid(true), + m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(), + m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { if (sc_ptr != nullptr) { m_sc = *sc_ptr; @@ -106,9 +104,9 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa, nullptr), m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), - m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0), - m_stop_id_is_valid(false), m_is_history_frame(false), - m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), + m_frame_base_error(), m_cfa_is_valid(true), + m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(), + m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(), m_mutex() { if (sc_ptr != nullptr) { m_sc = *sc_ptr; @@ -210,7 +208,7 @@ const Address &StackFrame::GetFrameCodeAddress() { bool StackFrame::ChangePC(addr_t pc) { std::lock_guard<std::recursive_mutex> guard(m_mutex); // We can't change the pc value of a history stack frame - it is immutable. - if (m_is_history_frame) + if (IsHistorical()) return false; m_frame_code_addr.SetRawAddress(pc); m_sc.Clear(false); @@ -266,7 +264,8 @@ Block *StackFrame::GetFrameBlock() { // StackFrame object, everyone will have as much information as possible and no // one will ever have to look things up manually. //---------------------------------------------------------------------- -const SymbolContext &StackFrame::GetSymbolContext(uint32_t resolve_scope) { +const SymbolContext & +StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) { std::lock_guard<std::recursive_mutex> guard(m_mutex); // Copy our internal symbol context into "sc". if ((m_flags.Get() & resolve_scope) != resolve_scope) { @@ -318,7 +317,7 @@ const SymbolContext &StackFrame::GetSymbolContext(uint32_t resolve_scope) { // haven't already tried to lookup one of those things. If we haven't // then we will do the query. - uint32_t actual_resolve_scope = 0; + SymbolContextItem actual_resolve_scope = SymbolContextItem(0); if (resolve_scope & eSymbolContextCompUnit) { if (m_flags.IsClear(eSymbolContextCompUnit)) { @@ -456,7 +455,7 @@ StackFrame::GetInScopeVariableList(bool get_file_globals, bool must_have_valid_location) { std::lock_guard<std::recursive_mutex> guard(m_mutex); // We can't fetch variable information for a history stack frame. - if (m_is_history_frame) + if (IsHistorical()) return VariableListSP(); VariableListSP var_list_sp(new VariableList); @@ -490,7 +489,7 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath( VariableSP &var_sp, Status &error) { llvm::StringRef original_var_expr = var_expr; // We can't fetch variable information for a history stack frame. - if (m_is_history_frame) + if (IsHistorical()) return ValueObjectSP(); if (var_expr.empty()) { @@ -1080,9 +1079,9 @@ bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress( exe_ctx.GetTargetPtr()); - if (m_sc.function->GetFrameBaseExpression().Evaluate( + if (!m_sc.function->GetFrameBaseExpression().Evaluate( &exe_ctx, nullptr, loclist_base_addr, nullptr, nullptr, - expr_value, &m_frame_base_error) == false) { + expr_value, &m_frame_base_error)) { // We should really have an error if evaluate returns, but in case we // don't, lets set the error to something at least. if (m_frame_base_error.Success()) @@ -1135,7 +1134,7 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { std::lock_guard<std::recursive_mutex> guard(m_mutex); ValueObjectSP valobj_sp; - if (m_is_history_frame) { + if (IsHistorical()) { return valobj_sp; } VariableList *var_list = GetVariableList(true); @@ -1164,7 +1163,7 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, ValueObjectSP StackFrame::TrackGlobalVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { std::lock_guard<std::recursive_mutex> guard(m_mutex); - if (m_is_history_frame) + if (IsHistorical()) return ValueObjectSP(); // Check to make sure we aren't already tracking this variable? @@ -1194,6 +1193,14 @@ bool StackFrame::IsInlined() { return false; } +bool StackFrame::IsHistorical() const { + return m_stack_frame_kind == StackFrame::Kind::History; +} + +bool StackFrame::IsArtificial() const { + return m_stack_frame_kind == StackFrame::Kind::Artificial; +} + lldb::LanguageType StackFrame::GetLanguage() { CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit; if (cu) @@ -1709,6 +1716,41 @@ lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, GetFrameCodeAddress()); } +lldb::ValueObjectSP StackFrame::FindVariable(ConstString name) { + ValueObjectSP value_sp; + + if (!name) + return value_sp; + + TargetSP target_sp = CalculateTarget(); + ProcessSP process_sp = CalculateProcess(); + + if (!target_sp && !process_sp) + return value_sp; + + VariableList variable_list; + VariableSP var_sp; + SymbolContext sc(GetSymbolContext(eSymbolContextBlock)); + + if (sc.block) { + const bool can_create = true; + const bool get_parent_variables = true; + const bool stop_if_block_is_inlined_function = true; + + if (sc.block->AppendVariables( + can_create, get_parent_variables, stop_if_block_is_inlined_function, + [this](Variable *v) { return v->IsInScope(this); }, + &variable_list)) { + var_sp = variable_list.FindVariable(name); + } + + if (var_sp) + value_sp = GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); + } + + return value_sp; +} + TargetSP StackFrame::CalculateTarget() { TargetSP target_sp; ThreadSP thread_sp(GetThread()); @@ -1910,3 +1952,11 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, } return true; } + +RecognizedStackFrameSP StackFrame::GetRecognizedFrame() { + if (!m_recognized_frame_sp) { + m_recognized_frame_sp = + StackFrameRecognizerManager::RecognizeFrame(CalculateStackFrame()); + } + return m_recognized_frame_sp; +} diff --git a/source/Target/StackFrameList.cpp b/source/Target/StackFrameList.cpp index 2380a91df41d..fc9fcec6c076 100644 --- a/source/Target/StackFrameList.cpp +++ b/source/Target/StackFrameList.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/StackFrameList.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" @@ -27,6 +23,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/Log.h" +#include "llvm/ADT/SmallPtrSet.h" //#define DEBUG_STACK_FRAMES 1 @@ -81,127 +78,119 @@ uint32_t StackFrameList::GetCurrentInlinedDepth() { } void StackFrameList::ResetCurrentInlinedDepth() { + if (!m_show_inlined_frames) + return; + std::lock_guard<std::recursive_mutex> guard(m_mutex); - if (m_show_inlined_frames) { - GetFramesUpTo(0); - if (m_frames.empty()) - return; - if (!m_frames[0]->IsInlined()) { - m_current_inlined_depth = UINT32_MAX; - m_current_inlined_pc = LLDB_INVALID_ADDRESS; - Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); - if (log && log->GetVerbose()) - log->Printf( - "ResetCurrentInlinedDepth: Invalidating current inlined depth.\n"); - } else { - // We only need to do something special about inlined blocks when we are - // at the beginning of an inlined function: - // FIXME: We probably also have to do something special if the PC is at - // the END - // of an inlined function, which coincides with the end of either its - // containing function or another inlined function. - - lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); - Block *block_ptr = m_frames[0]->GetFrameBlock(); - if (block_ptr) { - Address pc_as_address; - pc_as_address.SetLoadAddress(curr_pc, - &(m_thread.GetProcess()->GetTarget())); - AddressRange containing_range; - if (block_ptr->GetRangeContainingAddress(pc_as_address, - containing_range)) { - if (pc_as_address == containing_range.GetBaseAddress()) { - // If we got here because of a breakpoint hit, then set the inlined - // depth depending on where the breakpoint was set. If we got here - // because of a crash, then set the inlined depth to the deepest - // most block. Otherwise, we stopped here naturally as the result - // of a step, so set ourselves in the containing frame of the whole - // set of nested inlines, so the user can then "virtually" step - // into the frames one by one, or next over the whole mess. Note: - // We don't have to handle being somewhere in the middle of the - // stack here, since ResetCurrentInlinedDepth doesn't get called if - // there is a valid inlined depth set. - StopInfoSP stop_info_sp = m_thread.GetStopInfo(); - if (stop_info_sp) { - switch (stop_info_sp->GetStopReason()) { - case eStopReasonWatchpoint: - case eStopReasonException: - case eStopReasonExec: - case eStopReasonSignal: - // In all these cases we want to stop in the deepest most - // frame. - m_current_inlined_pc = curr_pc; - m_current_inlined_depth = 0; - break; - case eStopReasonBreakpoint: { - // FIXME: Figure out what this break point is doing, and set the - // inline depth - // appropriately. Be careful to take into account breakpoints - // that implement step over prologue, since that should do the - // default calculation. For now, if the breakpoints - // corresponding to this hit are all internal, - // I set the stop location to the top of the inlined stack, - // since that will make - // things like stepping over prologues work right. But if - // there are any non-internal breakpoints I do to the bottom of - // the stack, since that was the old behavior. - uint32_t bp_site_id = stop_info_sp->GetValue(); - BreakpointSiteSP bp_site_sp( - m_thread.GetProcess()->GetBreakpointSiteList().FindByID( - bp_site_id)); - bool all_internal = true; - if (bp_site_sp) { - uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); - for (uint32_t i = 0; i < num_owners; i++) { - Breakpoint &bp_ref = - bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); - if (!bp_ref.IsInternal()) { - all_internal = false; - } - } - } - if (!all_internal) { - m_current_inlined_pc = curr_pc; - m_current_inlined_depth = 0; - break; - } - } - LLVM_FALLTHROUGH; - default: { - // Otherwise, we should set ourselves at the container of the - // inlining, so that the user can descend into them. So first - // we check whether we have more than one inlined block sharing - // this PC: - int num_inlined_functions = 0; - - for (Block *container_ptr = block_ptr->GetInlinedParent(); - container_ptr != nullptr; - container_ptr = container_ptr->GetInlinedParent()) { - if (!container_ptr->GetRangeContainingAddress( - pc_as_address, containing_range)) - break; - if (pc_as_address != containing_range.GetBaseAddress()) - break; - - num_inlined_functions++; - } - m_current_inlined_pc = curr_pc; - m_current_inlined_depth = num_inlined_functions + 1; - Log *log( - lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); - if (log && log->GetVerbose()) - log->Printf("ResetCurrentInlinedDepth: setting inlined " - "depth: %d 0x%" PRIx64 ".\n", - m_current_inlined_depth, curr_pc); - - } break; - } - } - } + GetFramesUpTo(0); + if (m_frames.empty()) + return; + if (!m_frames[0]->IsInlined()) { + m_current_inlined_depth = UINT32_MAX; + m_current_inlined_pc = LLDB_INVALID_ADDRESS; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + log->Printf( + "ResetCurrentInlinedDepth: Invalidating current inlined depth.\n"); + return; + } + + // We only need to do something special about inlined blocks when we are + // at the beginning of an inlined function: + // FIXME: We probably also have to do something special if the PC is at + // the END of an inlined function, which coincides with the end of either + // its containing function or another inlined function. + + Block *block_ptr = m_frames[0]->GetFrameBlock(); + if (!block_ptr) + return; + + Address pc_as_address; + lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); + pc_as_address.SetLoadAddress(curr_pc, &(m_thread.GetProcess()->GetTarget())); + AddressRange containing_range; + if (!block_ptr->GetRangeContainingAddress(pc_as_address, containing_range) || + pc_as_address != containing_range.GetBaseAddress()) + return; + + // If we got here because of a breakpoint hit, then set the inlined depth + // depending on where the breakpoint was set. If we got here because of a + // crash, then set the inlined depth to the deepest most block. Otherwise, + // we stopped here naturally as the result of a step, so set ourselves in the + // containing frame of the whole set of nested inlines, so the user can then + // "virtually" step into the frames one by one, or next over the whole mess. + // Note: We don't have to handle being somewhere in the middle of the stack + // here, since ResetCurrentInlinedDepth doesn't get called if there is a + // valid inlined depth set. + StopInfoSP stop_info_sp = m_thread.GetStopInfo(); + if (!stop_info_sp) + return; + switch (stop_info_sp->GetStopReason()) { + case eStopReasonWatchpoint: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonSignal: + // In all these cases we want to stop in the deepest frame. + m_current_inlined_pc = curr_pc; + m_current_inlined_depth = 0; + break; + case eStopReasonBreakpoint: { + // FIXME: Figure out what this break point is doing, and set the inline + // depth appropriately. Be careful to take into account breakpoints that + // implement step over prologue, since that should do the default + // calculation. For now, if the breakpoints corresponding to this hit are + // all internal, I set the stop location to the top of the inlined stack, + // since that will make things like stepping over prologues work right. + // But if there are any non-internal breakpoints I do to the bottom of the + // stack, since that was the old behavior. + uint32_t bp_site_id = stop_info_sp->GetValue(); + BreakpointSiteSP bp_site_sp( + m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id)); + bool all_internal = true; + if (bp_site_sp) { + uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); + for (uint32_t i = 0; i < num_owners; i++) { + Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); + if (!bp_ref.IsInternal()) { + all_internal = false; } } } + if (!all_internal) { + m_current_inlined_pc = curr_pc; + m_current_inlined_depth = 0; + break; + } + } + LLVM_FALLTHROUGH; + default: { + // Otherwise, we should set ourselves at the container of the inlining, so + // that the user can descend into them. So first we check whether we have + // more than one inlined block sharing this PC: + int num_inlined_functions = 0; + + for (Block *container_ptr = block_ptr->GetInlinedParent(); + container_ptr != nullptr; + container_ptr = container_ptr->GetInlinedParent()) { + if (!container_ptr->GetRangeContainingAddress(pc_as_address, + containing_range)) + break; + if (pc_as_address != containing_range.GetBaseAddress()) + break; + + num_inlined_functions++; + } + m_current_inlined_pc = curr_pc; + m_current_inlined_depth = num_inlined_functions + 1; + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + log->Printf("ResetCurrentInlinedDepth: setting inlined " + "depth: %d 0x%" PRIx64 ".\n", + m_current_inlined_depth, curr_pc); + + break; + } } } @@ -226,8 +215,202 @@ void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) { m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC(); } +void StackFrameList::GetOnlyConcreteFramesUpTo(uint32_t end_idx, + Unwind *unwinder) { + assert(m_thread.IsValid() && "Expected valid thread"); + assert(m_frames.size() <= end_idx && "Expected there to be frames to fill"); + + if (end_idx < m_concrete_frames_fetched) + return; + + if (!unwinder) + return; + + uint32_t num_frames = unwinder->GetFramesUpTo(end_idx); + if (num_frames <= end_idx + 1) { + // Done unwinding. + m_concrete_frames_fetched = UINT32_MAX; + } + + // Don't create the frames eagerly. Defer this work to GetFrameAtIndex, + // which can lazily query the unwinder to create frames. + m_frames.resize(num_frames); +} + +/// Find the unique path through the call graph from \p begin (with return PC +/// \p return_pc) to \p end. On success this path is stored into \p path, and +/// on failure \p path is unchanged. +static void FindInterveningFrames(Function &begin, Function &end, + Target &target, addr_t return_pc, + std::vector<Function *> &path, + ModuleList &images, Log *log) { + LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}", + begin.GetDisplayName(), end.GetDisplayName(), return_pc); + + // Find a non-tail calling edge with the correct return PC. + auto first_level_edges = begin.GetCallEdges(); + if (log) + for (const CallEdge &edge : first_level_edges) + LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}", + edge.GetReturnPCAddress(begin, target)); + auto first_edge_it = std::lower_bound( + first_level_edges.begin(), first_level_edges.end(), return_pc, + [&](const CallEdge &edge, addr_t target_pc) { + return edge.GetReturnPCAddress(begin, target) < target_pc; + }); + if (first_edge_it == first_level_edges.end() || + first_edge_it->GetReturnPCAddress(begin, target) != return_pc) { + LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}", + begin.GetDisplayName(), return_pc); + return; + } + CallEdge &first_edge = const_cast<CallEdge &>(*first_edge_it); + + // The first callee may not be resolved, or there may be nothing to fill in. + Function *first_callee = first_edge.GetCallee(images); + if (!first_callee) { + LLDB_LOG(log, "Could not resolve callee"); + return; + } + if (first_callee == &end) { + LLDB_LOG(log, "Not searching further, first callee is {0} (retn-PC: {1:x})", + end.GetDisplayName(), return_pc); + return; + } + + // Run DFS on the tail-calling edges out of the first callee to find \p end. + // Fully explore the set of functions reachable from the first edge via tail + // calls in order to detect ambiguous executions. + struct DFS { + std::vector<Function *> active_path = {}; + std::vector<Function *> solution_path = {}; + llvm::SmallPtrSet<Function *, 2> visited_nodes = {}; + bool ambiguous = false; + Function *end; + ModuleList &images; + + DFS(Function *end, ModuleList &images) : end(end), images(images) {} + + void search(Function *first_callee, std::vector<Function *> &path) { + dfs(first_callee); + if (!ambiguous) + path = std::move(solution_path); + } + + void dfs(Function *callee) { + // Found a path to the target function. + if (callee == end) { + if (solution_path.empty()) + solution_path = active_path; + else + ambiguous = true; + return; + } + + // Terminate the search if tail recursion is found, or more generally if + // there's more than one way to reach a target. This errs on the side of + // caution: it conservatively stops searching when some solutions are + // still possible to save time in the average case. + if (!visited_nodes.insert(callee).second) { + ambiguous = true; + return; + } + + // Search the calls made from this callee. + active_path.push_back(callee); + for (CallEdge &edge : callee->GetTailCallingEdges()) { + Function *next_callee = edge.GetCallee(images); + if (!next_callee) + continue; + + dfs(next_callee); + if (ambiguous) + return; + } + active_path.pop_back(); + } + }; + + DFS(&end, images).search(first_callee, path); +} + +/// Given that \p next_frame will be appended to the frame list, synthesize +/// tail call frames between the current end of the list and \p next_frame. +/// If any frames are added, adjust the frame index of \p next_frame. +/// +/// -------------- +/// | ... | <- Completed frames. +/// -------------- +/// | prev_frame | +/// -------------- +/// | ... | <- Artificial frames inserted here. +/// -------------- +/// | next_frame | +/// -------------- +/// | ... | <- Not-yet-visited frames. +/// -------------- +void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) { + TargetSP target_sp = next_frame.CalculateTarget(); + if (!target_sp) + return; + + lldb::RegisterContextSP next_reg_ctx_sp = next_frame.GetRegisterContext(); + if (!next_reg_ctx_sp) + return; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + assert(!m_frames.empty() && "Cannot synthesize frames in an empty stack"); + StackFrame &prev_frame = *m_frames.back().get(); + + // Find the functions prev_frame and next_frame are stopped in. The function + // objects are needed to search the lazy call graph for intervening frames. + Function *prev_func = + prev_frame.GetSymbolContext(eSymbolContextFunction).function; + if (!prev_func) { + LLDB_LOG(log, "SynthesizeTailCallFrames: can't find previous function"); + return; + } + Function *next_func = + next_frame.GetSymbolContext(eSymbolContextFunction).function; + if (!next_func) { + LLDB_LOG(log, "SynthesizeTailCallFrames: can't find next function"); + return; + } + + // Try to find the unique sequence of (tail) calls which led from next_frame + // to prev_frame. + std::vector<Function *> path; + addr_t return_pc = next_reg_ctx_sp->GetPC(); + Target &target = *target_sp.get(); + ModuleList &images = next_frame.CalculateTarget()->GetImages(); + FindInterveningFrames(*next_func, *prev_func, target, return_pc, path, images, + log); + + // Push synthetic tail call frames. + for (Function *callee : llvm::reverse(path)) { + uint32_t frame_idx = m_frames.size(); + uint32_t concrete_frame_idx = next_frame.GetConcreteFrameIndex(); + addr_t cfa = LLDB_INVALID_ADDRESS; + bool cfa_is_valid = false; + addr_t pc = + callee->GetAddressRange().GetBaseAddress().GetLoadAddress(&target); + SymbolContext sc; + callee->CalculateSymbolContext(&sc); + auto synth_frame = std::make_shared<StackFrame>( + m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa, + cfa_is_valid, pc, StackFrame::Kind::Artificial, &sc); + m_frames.push_back(synth_frame); + LLDB_LOG(log, "Pushed frame {0}", callee->GetDisplayName()); + } + + // If any frames were created, adjust next_frame's index. + if (!path.empty()) + next_frame.SetFrameIndex(m_frames.size()); +} + void StackFrameList::GetFramesUpTo(uint32_t end_idx) { - // this makes sure we do not fetch frames for an invalid thread + // Do not fetch frames for an invalid thread. if (!m_thread.IsValid()) return; @@ -238,201 +421,189 @@ void StackFrameList::GetFramesUpTo(uint32_t end_idx) { Unwind *unwinder = m_thread.GetUnwinder(); - if (m_show_inlined_frames) { + if (!m_show_inlined_frames) { + GetOnlyConcreteFramesUpTo(end_idx, unwinder); + return; + } + #if defined(DEBUG_STACK_FRAMES) - StreamFile s(stdout, false); + StreamFile s(stdout, false); #endif - // If we are hiding some frames from the outside world, we need to add - // those onto the total count of frames to fetch. However, we don't need - // to do that if end_idx is 0 since in that case we always get the first - // concrete frame and all the inlined frames below it... And of course, if - // end_idx is UINT32_MAX that means get all, so just do that... - - uint32_t inlined_depth = 0; - if (end_idx > 0 && end_idx != UINT32_MAX) { - inlined_depth = GetCurrentInlinedDepth(); - if (inlined_depth != UINT32_MAX) { - if (end_idx > 0) - end_idx += inlined_depth; - } + // If we are hiding some frames from the outside world, we need to add + // those onto the total count of frames to fetch. However, we don't need + // to do that if end_idx is 0 since in that case we always get the first + // concrete frame and all the inlined frames below it... And of course, if + // end_idx is UINT32_MAX that means get all, so just do that... + + uint32_t inlined_depth = 0; + if (end_idx > 0 && end_idx != UINT32_MAX) { + inlined_depth = GetCurrentInlinedDepth(); + if (inlined_depth != UINT32_MAX) { + if (end_idx > 0) + end_idx += inlined_depth; } + } - StackFrameSP unwind_frame_sp; - do { - uint32_t idx = m_concrete_frames_fetched++; - 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 if we need - // to - if (m_frames.empty()) { - RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext()); - - if (reg_ctx_sp) { - const bool success = - unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); - // There shouldn't be any way not to get the frame info for frame - // 0. But if the unwinder can't make one, lets make one by hand - // with the - // SP as the CFA and see if that gets any further. - if (!success) { - cfa = reg_ctx_sp->GetSP(); - pc = reg_ctx_sp->GetPC(); - } - - unwind_frame_sp.reset(new StackFrame(m_thread.shared_from_this(), - m_frames.size(), idx, - reg_ctx_sp, cfa, pc, nullptr)); - m_frames.push_back(unwind_frame_sp); + StackFrameSP unwind_frame_sp; + do { + uint32_t idx = m_concrete_frames_fetched++; + 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 if we need + // to. + if (m_frames.empty()) { + RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext()); + + if (reg_ctx_sp) { + const bool success = + unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); + // There shouldn't be any way not to get the frame info for frame + // 0. But if the unwinder can't make one, lets make one by hand + // with the SP as the CFA and see if that gets any further. + if (!success) { + cfa = reg_ctx_sp->GetSP(); + pc = reg_ctx_sp->GetPC(); } - } else { - unwind_frame_sp = m_frames.front(); - cfa = unwind_frame_sp->m_id.GetCallFrameAddress(); + + unwind_frame_sp.reset(new StackFrame(m_thread.shared_from_this(), + m_frames.size(), idx, reg_ctx_sp, + cfa, pc, nullptr)); + m_frames.push_back(unwind_frame_sp); } } else { - const bool success = - unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); - if (!success) { - // We've gotten to the end of the stack. - SetAllFramesFetched(); - break; - } - const bool cfa_is_valid = true; - const bool stop_id_is_valid = false; - const bool is_history_frame = false; - unwind_frame_sp.reset(new StackFrame( - m_thread.shared_from_this(), m_frames.size(), idx, cfa, - cfa_is_valid, pc, 0, stop_id_is_valid, is_history_frame, nullptr)); - m_frames.push_back(unwind_frame_sp); + unwind_frame_sp = m_frames.front(); + cfa = unwind_frame_sp->m_id.GetCallFrameAddress(); } + } else { + const bool success = + unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); + if (!success) { + // We've gotten to the end of the stack. + SetAllFramesFetched(); + break; + } + const bool cfa_is_valid = true; + unwind_frame_sp.reset( + new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, cfa, + cfa_is_valid, pc, StackFrame::Kind::Regular, nullptr)); - assert(unwind_frame_sp); - SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext( - eSymbolContextBlock | eSymbolContextFunction); - Block *unwind_block = unwind_sc.block; - if (unwind_block) { - Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress()); - TargetSP target_sp = m_thread.CalculateTarget(); - // Be sure to adjust the frame address to match the address that was - // used to lookup the symbol context above. If we are in the first - // concrete frame, then we lookup using the current address, else we - // decrement the address by one to get the correct location. - if (idx > 0) { - if (curr_frame_address.GetOffset() == 0) { - // If curr_frame_address points to the first address in a section - // then after adjustment it will point to an other section. In that - // case resolve the address again to the correct section plus - // offset form. - addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress( - target_sp.get(), AddressClass::eCode); - curr_frame_address.SetOpcodeLoadAddress( - load_addr - 1, target_sp.get(), AddressClass::eCode); - } else { - curr_frame_address.Slide(-1); - } - } - - SymbolContext next_frame_sc; - Address next_frame_address; + // Create synthetic tail call frames between the previous frame and the + // newly-found frame. The new frame's index may change after this call, + // although its concrete index will stay the same. + SynthesizeTailCallFrames(*unwind_frame_sp.get()); - while (unwind_sc.GetParentOfInlinedScope( - curr_frame_address, next_frame_sc, next_frame_address)) { - next_frame_sc.line_entry.ApplyFileMappings(target_sp); - StackFrameSP frame_sp( - new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, - unwind_frame_sp->GetRegisterContextSP(), cfa, - next_frame_address, &next_frame_sc)); + m_frames.push_back(unwind_frame_sp); + } - m_frames.push_back(frame_sp); - unwind_sc = next_frame_sc; - curr_frame_address = next_frame_address; + assert(unwind_frame_sp); + SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext( + eSymbolContextBlock | eSymbolContextFunction); + Block *unwind_block = unwind_sc.block; + if (unwind_block) { + Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress()); + TargetSP target_sp = m_thread.CalculateTarget(); + // Be sure to adjust the frame address to match the address that was + // used to lookup the symbol context above. If we are in the first + // concrete frame, then we lookup using the current address, else we + // decrement the address by one to get the correct location. + if (idx > 0) { + if (curr_frame_address.GetOffset() == 0) { + // If curr_frame_address points to the first address in a section + // then after adjustment it will point to an other section. In that + // case resolve the address again to the correct section plus + // offset form. + addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress( + target_sp.get(), AddressClass::eCode); + curr_frame_address.SetOpcodeLoadAddress( + load_addr - 1, target_sp.get(), AddressClass::eCode); + } else { + curr_frame_address.Slide(-1); } } - } while (m_frames.size() - 1 < end_idx); - // Don't try to merge till you've calculated all the frames in this stack. - if (GetAllFramesFetched() && m_prev_frames_sp) { - StackFrameList *prev_frames = m_prev_frames_sp.get(); - StackFrameList *curr_frames = this; + SymbolContext next_frame_sc; + Address next_frame_address; + + while (unwind_sc.GetParentOfInlinedScope( + curr_frame_address, next_frame_sc, next_frame_address)) { + next_frame_sc.line_entry.ApplyFileMappings(target_sp); + StackFrameSP frame_sp( + new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, + unwind_frame_sp->GetRegisterContextSP(), cfa, + next_frame_address, &next_frame_sc)); + + m_frames.push_back(frame_sp); + unwind_sc = next_frame_sc; + curr_frame_address = next_frame_address; + } + } + } while (m_frames.size() - 1 < end_idx); -// curr_frames->m_current_inlined_depth = prev_frames->m_current_inlined_depth; -// curr_frames->m_current_inlined_pc = prev_frames->m_current_inlined_pc; -// printf ("GetFramesUpTo: Copying current inlined depth: %d 0x%" PRIx64 ".\n", -// curr_frames->m_current_inlined_depth, curr_frames->m_current_inlined_pc); + // Don't try to merge till you've calculated all the frames in this stack. + if (GetAllFramesFetched() && m_prev_frames_sp) { + StackFrameList *prev_frames = m_prev_frames_sp.get(); + StackFrameList *curr_frames = this; #if defined(DEBUG_STACK_FRAMES) - s.PutCString("\nprev_frames:\n"); - prev_frames->Dump(&s); - s.PutCString("\ncurr_frames:\n"); - curr_frames->Dump(&s); - s.EOL(); + s.PutCString("\nprev_frames:\n"); + prev_frames->Dump(&s); + s.PutCString("\ncurr_frames:\n"); + curr_frames->Dump(&s); + s.EOL(); #endif - size_t curr_frame_num, prev_frame_num; + size_t curr_frame_num, prev_frame_num; - for (curr_frame_num = curr_frames->m_frames.size(), - prev_frame_num = prev_frames->m_frames.size(); - curr_frame_num > 0 && prev_frame_num > 0; - --curr_frame_num, --prev_frame_num) { - const size_t curr_frame_idx = curr_frame_num - 1; - const size_t prev_frame_idx = prev_frame_num - 1; - StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]); - StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]); + for (curr_frame_num = curr_frames->m_frames.size(), + prev_frame_num = prev_frames->m_frames.size(); + curr_frame_num > 0 && prev_frame_num > 0; + --curr_frame_num, --prev_frame_num) { + const size_t curr_frame_idx = curr_frame_num - 1; + const size_t prev_frame_idx = prev_frame_num - 1; + StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]); + StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]); #if defined(DEBUG_STACK_FRAMES) - s.Printf("\n\nCurr frame #%u ", curr_frame_idx); - if (curr_frame_sp) - curr_frame_sp->Dump(&s, true, false); - else - s.PutCString("NULL"); - s.Printf("\nPrev frame #%u ", prev_frame_idx); - if (prev_frame_sp) - prev_frame_sp->Dump(&s, true, false); - else - s.PutCString("NULL"); + s.Printf("\n\nCurr frame #%u ", curr_frame_idx); + if (curr_frame_sp) + curr_frame_sp->Dump(&s, true, false); + else + s.PutCString("NULL"); + s.Printf("\nPrev frame #%u ", prev_frame_idx); + if (prev_frame_sp) + prev_frame_sp->Dump(&s, true, false); + else + s.PutCString("NULL"); #endif - StackFrame *curr_frame = curr_frame_sp.get(); - StackFrame *prev_frame = prev_frame_sp.get(); + StackFrame *curr_frame = curr_frame_sp.get(); + StackFrame *prev_frame = prev_frame_sp.get(); - if (curr_frame == nullptr || prev_frame == nullptr) - break; + if (curr_frame == nullptr || prev_frame == nullptr) + break; - // Check the stack ID to make sure they are equal - if (curr_frame->GetStackID() != prev_frame->GetStackID()) - break; + // Check the stack ID to make sure they are equal. + if (curr_frame->GetStackID() != prev_frame->GetStackID()) + break; - prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame); - // Now copy the fixed up previous frame into the current frames so the - // pointer doesn't change - m_frames[curr_frame_idx] = prev_frame_sp; -// curr_frame->UpdateCurrentFrameFromPreviousFrame (*prev_frame); + prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame); + // Now copy the fixed up previous frame into the current frames so the + // pointer doesn't change. + m_frames[curr_frame_idx] = prev_frame_sp; #if defined(DEBUG_STACK_FRAMES) - s.Printf("\n Copying previous frame to current frame"); + s.Printf("\n Copying previous frame to current frame"); #endif - } - // We are done with the old stack frame list, we can release it now - m_prev_frames_sp.reset(); } + // We are done with the old stack frame list, we can release it now. + m_prev_frames_sp.reset(); + } #if defined(DEBUG_STACK_FRAMES) - s.PutCString("\n\nNew frames:\n"); - Dump(&s); - s.EOL(); + s.PutCString("\n\nNew frames:\n"); + Dump(&s); + s.EOL(); #endif - } else { - if (end_idx < m_concrete_frames_fetched) - return; - - if (unwinder) { - uint32_t num_frames = unwinder->GetFramesUpTo(end_idx); - if (num_frames <= end_idx + 1) { - // Done unwinding. - m_concrete_frames_fetched = UINT32_MAX; - } - m_frames.resize(num_frames); - } - } } uint32_t StackFrameList::GetNumFrames(bool can_create) { @@ -441,11 +612,7 @@ uint32_t StackFrameList::GetNumFrames(bool can_create) { if (can_create) GetFramesUpTo(UINT32_MAX); - uint32_t inlined_depth = GetCurrentInlinedDepth(); - if (inlined_depth == UINT32_MAX) - return m_frames.size(); - else - return m_frames.size() - inlined_depth; + return GetVisibleStackFrameIndex(m_frames.size()); } void StackFrameList::Dump(Stream *s) { @@ -497,11 +664,9 @@ StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) { addr_t pc, cfa; if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) { const bool cfa_is_valid = true; - const bool stop_id_is_valid = false; - const bool is_history_frame = false; - frame_sp.reset(new StackFrame( - m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, 0, - stop_id_is_valid, is_history_frame, nullptr)); + frame_sp.reset(new StackFrame(m_thread.shared_from_this(), idx, idx, + cfa, cfa_is_valid, pc, + StackFrame::Kind::Regular, nullptr)); Function *function = frame_sp->GetSymbolContext(eSymbolContextFunction).function; @@ -576,9 +741,6 @@ StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) { if ((*pos)->GetStackID() == stack_id) return *pos; } - - // if (m_frames.back()->GetStackID() < stack_id) - // frame_idx = m_frames.size(); } do { frame_sp = GetFrameAtIndex(frame_idx); @@ -625,7 +787,6 @@ uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) { return m_selected_frame_idx; } -// Mark a stack frame as the current frame using the frame index bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) { std::lock_guard<std::recursive_mutex> guard(m_mutex); StackFrameSP frame_sp(GetFrameAtIndex(idx)); @@ -657,19 +818,6 @@ void StackFrameList::Clear() { m_concrete_frames_fetched = 0; } -void StackFrameList::InvalidateFrames(uint32_t start_idx) { - std::lock_guard<std::recursive_mutex> guard(m_mutex); - if (m_show_inlined_frames) { - Clear(); - } else { - const size_t num_frames = m_frames.size(); - while (start_idx < num_frames) { - m_frames[start_idx].reset(); - ++start_idx; - } - } -} - void StackFrameList::Merge(std::unique_ptr<StackFrameList> &curr_ap, lldb::StackFrameListSP &prev_sp) { std::unique_lock<std::recursive_mutex> current_lock, previous_lock; diff --git a/source/Target/StackFrameRecognizer.cpp b/source/Target/StackFrameRecognizer.cpp new file mode 100644 index 000000000000..152f4a198e20 --- /dev/null +++ b/source/Target/StackFrameRecognizer.cpp @@ -0,0 +1,191 @@ +//===-- StackFrameRecognizer.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <vector> +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackFrameRecognizer.h" +#include "lldb/Utility/RegularExpression.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef LLDB_DISABLE_PYTHON + +class ScriptedRecognizedStackFrame : public RecognizedStackFrame { +public: + ScriptedRecognizedStackFrame(ValueObjectListSP args) { + m_arguments = args; + } +}; + +ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer( + ScriptInterpreter *interpreter, const char *pclass) + : m_interpreter(interpreter), m_python_class(pclass) { + m_python_object_sp = + m_interpreter->CreateFrameRecognizer(m_python_class.c_str()); +} + +RecognizedStackFrameSP +ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) { + if (!m_python_object_sp || !m_interpreter) + return RecognizedStackFrameSP(); + + ValueObjectListSP args = + m_interpreter->GetRecognizedArguments(m_python_object_sp, frame); + + return RecognizedStackFrameSP(new ScriptedRecognizedStackFrame(args)); +} + +#endif + +class StackFrameRecognizerManagerImpl { +public: + void AddRecognizer(StackFrameRecognizerSP recognizer, + const ConstString &module, const ConstString &symbol, + bool first_instruction_only) { + m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, false, module, RegularExpressionSP(), + symbol, RegularExpressionSP(), + first_instruction_only}); + } + + void AddRecognizer(StackFrameRecognizerSP recognizer, + RegularExpressionSP module, RegularExpressionSP symbol, + bool first_instruction_only) { + m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, true, ConstString(), module, + ConstString(), symbol, first_instruction_only}); + } + + void ForEach( + std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module, + std::string symbol, bool regexp)> const &callback) { + for (auto entry : m_recognizers) { + if (entry.is_regexp) { + callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module_regexp->GetText(), + entry.symbol_regexp->GetText(), true); + } else { + callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module.GetCString(), + entry.symbol.GetCString(), false); + } + } + } + + bool RemoveRecognizerWithID(uint32_t recognizer_id) { + if (recognizer_id >= m_recognizers.size()) return false; + if (m_recognizers[recognizer_id].deleted) return false; + m_recognizers[recognizer_id].deleted = true; + return true; + } + + void RemoveAllRecognizers() { + m_recognizers.clear(); + } + + StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) { + const SymbolContext &symctx = + frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction); + ConstString function_name = symctx.GetFunctionName(); + ModuleSP module_sp = symctx.module_sp; + if (!module_sp) return StackFrameRecognizerSP(); + ConstString module_name = module_sp->GetFileSpec().GetFilename(); + Symbol *symbol = symctx.symbol; + if (!symbol) return StackFrameRecognizerSP(); + Address start_addr = symbol->GetAddress(); + Address current_addr = frame->GetFrameCodeAddress(); + + for (auto entry : m_recognizers) { + if (entry.deleted) continue; + if (entry.module) + if (entry.module != module_name) continue; + + if (entry.module_regexp) + if (!entry.module_regexp->Execute(module_name.GetStringRef())) continue; + + if (entry.symbol) + if (entry.symbol != function_name) continue; + + if (entry.symbol_regexp) + if (!entry.symbol_regexp->Execute(function_name.GetStringRef())) + continue; + + if (entry.first_instruction_only) + if (start_addr != current_addr) continue; + + return entry.recognizer; + } + return StackFrameRecognizerSP(); + } + + RecognizedStackFrameSP RecognizeFrame(StackFrameSP frame) { + auto recognizer = GetRecognizerForFrame(frame); + if (!recognizer) return RecognizedStackFrameSP(); + return recognizer->RecognizeFrame(frame); + } + + private: + struct RegisteredEntry { + uint32_t recognizer_id; + bool deleted; + StackFrameRecognizerSP recognizer; + bool is_regexp; + ConstString module; + RegularExpressionSP module_regexp; + ConstString symbol; + RegularExpressionSP symbol_regexp; + bool first_instruction_only; + }; + + std::deque<RegisteredEntry> m_recognizers; +}; + +StackFrameRecognizerManagerImpl &GetStackFrameRecognizerManagerImpl() { + static StackFrameRecognizerManagerImpl instance = + StackFrameRecognizerManagerImpl(); + return instance; +} + +void StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP recognizer, const ConstString &module, + const ConstString &symbol, bool first_instruction_only) { + GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol, + first_instruction_only); +} + +void StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP recognizer, RegularExpressionSP module, + RegularExpressionSP symbol, bool first_instruction_only) { + GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol, + first_instruction_only); +} + +void StackFrameRecognizerManager::ForEach( + std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module, + std::string symbol, bool regexp)> const &callback) { + GetStackFrameRecognizerManagerImpl().ForEach(callback); +} + +void StackFrameRecognizerManager::RemoveAllRecognizers() { + GetStackFrameRecognizerManagerImpl().RemoveAllRecognizers(); +} + +bool StackFrameRecognizerManager::RemoveRecognizerWithID(uint32_t recognizer_id) { + return GetStackFrameRecognizerManagerImpl().RemoveRecognizerWithID(recognizer_id); +} + +RecognizedStackFrameSP StackFrameRecognizerManager::RecognizeFrame( + StackFrameSP frame) { + return GetStackFrameRecognizerManagerImpl().RecognizeFrame(frame); +} + +StackFrameRecognizerSP StackFrameRecognizerManager::GetRecognizerForFrame( + lldb::StackFrameSP frame) { + return GetStackFrameRecognizerManagerImpl().GetRecognizerForFrame(frame); +} diff --git a/source/Target/StackID.cpp b/source/Target/StackID.cpp index 341c902af995..d6640b24ad5c 100644 --- a/source/Target/StackID.cpp +++ b/source/Target/StackID.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/StackID.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Symbol.h" diff --git a/source/Target/StopInfo.cpp b/source/Target/StopInfo.cpp index f8b17dc10eca..4e13c588b484 100644 --- a/source/Target/StopInfo.cpp +++ b/source/Target/StopInfo.cpp @@ -7,12 +7,8 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes #include <string> -// Other libraries and framework includes -// Project includes #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" @@ -333,6 +329,19 @@ protected: // commands when we see the same breakpoint hit a second time. m_should_stop_is_valid = true; + + // It is possible that the user has a breakpoint at the same site + // as the completed plan had (e.g. user has a breakpoint + // on a module entry point, and `ThreadPlanCallFunction` ends + // also there). We can't find an internal breakpoint in the loop + // later because it was already removed on the plan completion. + // So check if the plan was completed, and stop if so. + if (thread_sp->CompletedPlanOverridesBreakpoint()) { + m_should_stop = true; + thread_sp->ResetStopInfo(); + return; + } + if (log) log->Printf("StopInfoBreakpoint::PerformAction - Hit a " "breakpoint while running an expression," @@ -358,9 +367,8 @@ protected: "continuing: %s.", m_should_stop ? "true" : "false"); process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf( - "Warning: hit breakpoint while " - "running function, skipping commands and conditions to prevent " - "recursion."); + "Warning: hit breakpoint while running function, skipping " + "commands and conditions to prevent recursion.\n"); return; } @@ -534,9 +542,9 @@ protected: __FUNCTION__, m_value); } - if ((m_should_stop == false || internal_breakpoint) - && thread_sp->CompletedPlanOverridesBreakpoint()) { - + if ((!m_should_stop || internal_breakpoint) && + thread_sp->CompletedPlanOverridesBreakpoint()) { + // Override should_stop decision when we have completed step plan // additionally to the breakpoint m_should_stop = true; @@ -720,14 +728,18 @@ protected: StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo(); assert(stored_stop_info_sp.get() == this); + Status new_plan_status; ThreadPlanSP new_plan_sp( thread_sp->QueueThreadPlanForStepSingleInstruction( - false, // step-over - false, // abort_other_plans - true)); // stop_other_threads - new_plan_sp->SetIsMasterPlan(true); - new_plan_sp->SetOkayToDiscard(false); - new_plan_sp->SetPrivate(true); + false, // step-over + false, // abort_other_plans + true, // stop_other_threads + new_plan_status)); + if (new_plan_sp && new_plan_status.Success()) { + new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetOkayToDiscard(false); + new_plan_sp->SetPrivate(true); + } process_sp->GetThreadList().SetSelectedThreadByID( thread_sp->GetID()); process_sp->ResumeSynchronous(nullptr); diff --git a/source/Target/SystemRuntime.cpp b/source/Target/SystemRuntime.cpp index 3fdf6daa6afa..574c01cb5ae1 100644 --- a/source/Target/SystemRuntime.cpp +++ b/source/Target/SystemRuntime.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/SystemRuntime.h" #include "lldb/Core/PluginManager.h" #include "lldb/Target/Process.h" diff --git a/source/Target/Target.cpp b/source/Target/Target.cpp index 3f70741713fb..437f92abdab3 100644 --- a/source/Target/Target.cpp +++ b/source/Target/Target.cpp @@ -7,11 +7,7 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -#include <mutex> -// Other libraries and framework includes -// Project includes +#include "lldb/Target/Target.h" #include "Plugins/ExpressionParser/Clang/ClangASTSource.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" @@ -21,16 +17,17 @@ #include "lldb/Breakpoint/BreakpointResolverFileLine.h" #include "lldb/Breakpoint/BreakpointResolverFileRegex.h" #include "lldb/Breakpoint/BreakpointResolverName.h" +#include "lldb/Breakpoint/BreakpointResolverScripted.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/Event.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/SearchFilter.h" #include "lldb/Core/Section.h" #include "lldb/Core/SourceManager.h" -#include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/REPL.h" #include "lldb/Expression/UserExpression.h" @@ -52,14 +49,16 @@ #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/SystemRuntime.h" -#include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" +#include "lldb/Utility/Event.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" +#include <mutex> using namespace lldb; using namespace lldb_private; @@ -195,6 +194,8 @@ void Target::DeleteCurrentProcess() { const lldb::ProcessSP &Target::CreateProcess(ListenerSP listener_sp, llvm::StringRef plugin_name, const FileSpec *crash_file) { + if (!listener_sp) + listener_sp = GetDebugger().GetListener(); DeleteCurrentProcess(); m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name, listener_sp, crash_file); @@ -322,7 +323,7 @@ BreakpointSP Target::CreateSourceRegexBreakpoint( BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, const FileSpec &file, uint32_t line_no, - lldb::addr_t offset, + uint32_t column, lldb::addr_t offset, LazyBool check_inlines, LazyBool skip_prologue, bool internal, bool hardware, @@ -366,8 +367,8 @@ BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine( - nullptr, remapped_file, line_no, offset, check_inlines, skip_prologue, - !static_cast<bool>(move_to_nearest_code))); + nullptr, remapped_file, line_no, column, offset, check_inlines, + skip_prologue, !static_cast<bool>(move_to_nearest_code))); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } @@ -412,12 +413,11 @@ Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal, false); } -BreakpointSP -Target::CreateBreakpoint(const FileSpecList *containingModules, - const FileSpecList *containingSourceFiles, - const char *func_name, uint32_t func_name_type_mask, - LanguageType language, lldb::addr_t offset, - LazyBool skip_prologue, bool internal, bool hardware) { +BreakpointSP Target::CreateBreakpoint( + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, const char *func_name, + FunctionNameType func_name_type_mask, LanguageType language, + lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; if (func_name) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( @@ -440,9 +440,9 @@ lldb::BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const std::vector<std::string> &func_names, - uint32_t func_name_type_mask, LanguageType language, - lldb::addr_t offset, LazyBool skip_prologue, - bool internal, bool hardware) { + FunctionNameType func_name_type_mask, + LanguageType language, lldb::addr_t offset, + LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; size_t num_names = func_names.size(); if (num_names > 0) { @@ -462,11 +462,13 @@ Target::CreateBreakpoint(const FileSpecList *containingModules, return bp_sp; } -BreakpointSP Target::CreateBreakpoint( - const FileSpecList *containingModules, - const FileSpecList *containingSourceFiles, const char *func_names[], - size_t num_names, uint32_t func_name_type_mask, LanguageType language, - lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { +BreakpointSP +Target::CreateBreakpoint(const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const char *func_names[], size_t num_names, + FunctionNameType func_name_type_mask, + LanguageType language, lldb::addr_t offset, + LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; if (num_names > 0) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( @@ -579,13 +581,56 @@ Target::CreateExceptionBreakpoint(enum lldb::LanguageType language, return exc_bkpt_sp; } +lldb::BreakpointSP +Target::CreateScriptedBreakpoint(const llvm::StringRef class_name, + const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + bool internal, + bool request_hardware, + StructuredData::ObjectSP extra_args_sp, + Status *creation_error) +{ + SearchFilterSP filter_sp; + + lldb::SearchDepth depth = lldb::eSearchDepthTarget; + bool has_files = containingSourceFiles && containingSourceFiles->GetSize() > 0; + bool has_modules = containingModules && containingModules->GetSize() > 0; + + if (has_files && has_modules) { + filter_sp = GetSearchFilterForModuleAndCUList( + containingModules, containingSourceFiles); + } else if (has_files) { + filter_sp = GetSearchFilterForModuleAndCUList( + nullptr, containingSourceFiles); + } else if (has_modules) { + filter_sp = GetSearchFilterForModuleList(containingModules); + } else { + filter_sp.reset(new SearchFilterForUnconstrainedSearches(shared_from_this())); + } + + StructuredDataImpl *extra_args_impl = new StructuredDataImpl(); + if (extra_args_sp) + extra_args_impl->SetObjectSP(extra_args_sp); + + BreakpointResolverSP resolver_sp(new + BreakpointResolverScripted(nullptr, class_name, + depth, + extra_args_impl, + *GetDebugger().GetCommandInterpreter() + .GetScriptInterpreter())); + return CreateBreakpoint(filter_sp, resolver_sp, internal, false, true); + +} + + BreakpointSP Target::CreateBreakpoint(SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal, bool request_hardware, bool resolve_indirect_symbols) { BreakpointSP bp_sp; if (filter_sp && resolver_sp) { - bp_sp.reset(new Breakpoint(*this, filter_sp, resolver_sp, request_hardware, + const bool hardware = request_hardware || GetRequireHardwareBreakpoints(); + bp_sp.reset(new Breakpoint(*this, filter_sp, resolver_sp, hardware, resolve_indirect_symbols)); resolver_sp->SetBreakpoint(bp_sp.get()); AddBreakpoint(bp_sp, internal); @@ -719,17 +764,23 @@ void Target::GetBreakpointNames(std::vector<std::string> &names) for (auto bp_name : m_breakpoint_names) { names.push_back(bp_name.first.AsCString()); } - std::sort(names.begin(), names.end()); + llvm::sort(names.begin(), names.end()); } bool Target::ProcessIsValid() { return (m_process_sp && m_process_sp->IsAlive()); } -static bool CheckIfWatchpointsExhausted(Target *target, Status &error) { +static bool CheckIfWatchpointsSupported(Target *target, Status &error) { uint32_t num_supported_hardware_watchpoints; Status rc = target->GetProcessSP()->GetWatchpointSupportInfo( num_supported_hardware_watchpoints); + + // If unable to determine the # of watchpoints available, + // assume they are supported. + if (rc.Fail()) + return true; + if (num_supported_hardware_watchpoints == 0) { error.SetErrorStringWithFormat( "Target supports (%u) hardware watchpoint slots.\n", @@ -768,7 +819,7 @@ WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size, error.SetErrorStringWithFormat("invalid watchpoint type: %d", kind); } - if (!CheckIfWatchpointsExhausted(this, error)) + if (!CheckIfWatchpointsSupported(this, error)) return wp_sp; // Currently we only support one watchpoint per address, with total number of @@ -1377,7 +1428,7 @@ void Target::DidExec() { } void Target::SetExecutableModule(ModuleSP &executable_sp, - bool get_dependent_files) { + LoadDependentFiles load_dependent_files) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); ClearModules(false); @@ -1401,8 +1452,20 @@ void Target::SetExecutableModule(ModuleSP &executable_sp, FileSpecList dependent_files; ObjectFile *executable_objfile = executable_sp->GetObjectFile(); + bool load_dependents = true; + switch (load_dependent_files) { + case eLoadDependentsDefault: + load_dependents = executable_sp->IsExecutable(); + break; + case eLoadDependentsYes: + load_dependents = true; + break; + case eLoadDependentsNo: + load_dependents = false; + break; + } - if (executable_objfile && get_dependent_files) { + if (executable_objfile && load_dependents) { executable_objfile->GetDependentModules(dependent_files); for (uint32_t i = 0; i < dependent_files.GetSize(); i++) { FileSpec dependent_file_spec( @@ -1426,13 +1489,33 @@ void Target::SetExecutableModule(ModuleSP &executable_sp, } } -bool Target::SetArchitecture(const ArchSpec &arch_spec) { +bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); bool missing_local_arch = !m_arch.GetSpec().IsValid(); bool replace_local_arch = true; bool compatible_local_arch = false; ArchSpec other(arch_spec); + // Changing the architecture might mean that the currently selected platform + // isn't compatible. Set the platform correctly if we are asked to do so, + // otherwise assume the user will set the platform manually. + if (set_platform) { + if (other.IsValid()) { + auto platform_sp = GetPlatform(); + if (!platform_sp || + !platform_sp->IsCompatibleArchitecture(other, false, nullptr)) { + ArchSpec platform_arch; + auto arch_platform_sp = + Platform::GetPlatformForArchitecture(other, &platform_arch); + if (arch_platform_sp) { + SetPlatform(arch_platform_sp); + if (platform_arch.IsValid()) + other = platform_arch; + } + } + } + } + if (!missing_local_arch) { if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { other.MergeFrom(m_arch.GetSpec()); @@ -1487,7 +1570,7 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec) { nullptr, nullptr); if (!error.Fail() && executable_sp) { - SetExecutableModule(executable_sp, true); + SetExecutableModule(executable_sp, eLoadDependentsYes); return true; } } @@ -1495,11 +1578,18 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec) { } bool Target::MergeArchitecture(const ArchSpec &arch_spec) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET)); if (arch_spec.IsValid()) { if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { // The current target arch is compatible with "arch_spec", see if we can // improve our current architecture using bits from "arch_spec" + if (log) + log->Printf("Target::MergeArchitecture target has arch %s, merging with " + "arch %s", + m_arch.GetSpec().GetTriple().getTriple().c_str(), + arch_spec.GetTriple().getTriple().c_str()); + // Merge bits from arch_spec into "merged_arch" and set our architecture ArchSpec merged_arch(m_arch.GetSpec()); merged_arch.MergeFrom(arch_spec); @@ -2057,7 +2147,7 @@ void Target::ImageSearchPathsChanged(const PathMappingList &path_list, Target *target = (Target *)baton; ModuleSP exe_module_sp(target->GetExecutableModule()); if (exe_module_sp) - target->SetExecutableModule(exe_module_sp, true); + target->SetExecutableModule(exe_module_sp, eLoadDependentsYes); } TypeSystem *Target::GetScratchTypeSystemForLanguage(Status *error, @@ -2351,249 +2441,22 @@ lldb::addr_t Target::GetPersistentSymbol(const ConstString &name) { lldb::addr_t Target::GetCallableLoadAddress(lldb::addr_t load_addr, AddressClass addr_class) const { - addr_t code_addr = load_addr; - switch (m_arch.GetSpec().GetMachine()) { - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - switch (addr_class) { - case AddressClass::eData: - case AddressClass::eDebug: - return LLDB_INVALID_ADDRESS; - - case AddressClass::eUnknown: - case AddressClass::eInvalid: - case AddressClass::eCode: - case AddressClass::eCodeAlternateISA: - case AddressClass::eRuntime: - if ((code_addr & 2ull) || (addr_class == AddressClass::eCodeAlternateISA)) - code_addr |= 1ull; - break; - } - break; - - case llvm::Triple::arm: - case llvm::Triple::thumb: - switch (addr_class) { - case AddressClass::eData: - case AddressClass::eDebug: - return LLDB_INVALID_ADDRESS; - - case AddressClass::eUnknown: - case AddressClass::eInvalid: - case AddressClass::eCode: - case AddressClass::eCodeAlternateISA: - case AddressClass::eRuntime: - // Check if bit zero it no set? - if ((code_addr & 1ull) == 0) { - // Bit zero isn't set, check if the address is a multiple of 2? - if (code_addr & 2ull) { - // The address is a multiple of 2 so it must be thumb, set bit zero - code_addr |= 1ull; - } else if (addr_class == AddressClass::eCodeAlternateISA) { - // We checked the address and the address claims to be the alternate - // ISA which means thumb, so set bit zero. - code_addr |= 1ull; - } - } - break; - } - break; - - default: - break; - } - return code_addr; + auto arch_plugin = GetArchitecturePlugin(); + return arch_plugin ? + arch_plugin->GetCallableLoadAddress(load_addr, addr_class) : load_addr; } lldb::addr_t Target::GetOpcodeLoadAddress(lldb::addr_t load_addr, AddressClass addr_class) const { - addr_t opcode_addr = load_addr; - switch (m_arch.GetSpec().GetMachine()) { - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - case llvm::Triple::arm: - case llvm::Triple::thumb: - switch (addr_class) { - case AddressClass::eData: - case AddressClass::eDebug: - return LLDB_INVALID_ADDRESS; - - case AddressClass::eInvalid: - case AddressClass::eUnknown: - case AddressClass::eCode: - case AddressClass::eCodeAlternateISA: - case AddressClass::eRuntime: - opcode_addr &= ~(1ull); - break; - } - break; - - default: - break; - } - return opcode_addr; + auto arch_plugin = GetArchitecturePlugin(); + return arch_plugin ? + arch_plugin->GetOpcodeLoadAddress(load_addr, addr_class) : load_addr; } lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) { - addr_t breakable_addr = addr; - Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); - - switch (m_arch.GetSpec().GetMachine()) { - default: - break; - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: { - addr_t function_start = 0; - addr_t current_offset = 0; - uint32_t loop_count = 0; - Address resolved_addr; - uint32_t arch_flags = m_arch.GetSpec().GetFlags(); - bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16; - bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips; - SectionLoadList §ion_load_list = GetSectionLoadList(); - - if (section_load_list.IsEmpty()) - // No sections are loaded, so we must assume we are not running yet and - // need to operate only on file address. - m_images.ResolveFileAddress(addr, resolved_addr); - else - section_load_list.ResolveLoadAddress(addr, resolved_addr); - - // Get the function boundaries to make sure we don't scan back before the - // beginning of the current function. - ModuleSP temp_addr_module_sp(resolved_addr.GetModule()); - if (temp_addr_module_sp) { - SymbolContext sc; - uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; - temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr, - resolve_scope, sc); - Address sym_addr; - if (sc.function) - sym_addr = sc.function->GetAddressRange().GetBaseAddress(); - else if (sc.symbol) - sym_addr = sc.symbol->GetAddress(); - - function_start = sym_addr.GetLoadAddress(this); - if (function_start == LLDB_INVALID_ADDRESS) - function_start = sym_addr.GetFileAddress(); - - if (function_start) - current_offset = addr - function_start; - } - - // If breakpoint address is start of function then we dont have to do - // anything. - if (current_offset == 0) - return breakable_addr; - else - loop_count = current_offset / 2; - - if (loop_count > 3) { - // Scan previous 6 bytes - if (IsMips16 | IsMicromips) - loop_count = 3; - // For mips-only, instructions are always 4 bytes, so scan previous 4 - // bytes only. - else - loop_count = 2; - } - - // Create Disassembler Instance - lldb::DisassemblerSP disasm_sp( - Disassembler::FindPlugin(m_arch.GetSpec(), nullptr, nullptr)); - - ExecutionContext exe_ctx; - CalculateExecutionContext(exe_ctx); - InstructionList instruction_list; - InstructionSP prev_insn; - bool prefer_file_cache = true; // Read from file - uint32_t inst_to_choose = 0; - - for (uint32_t i = 1; i <= loop_count; i++) { - // Adjust the address to read from. - resolved_addr.Slide(-2); - AddressRange range(resolved_addr, i * 2); - uint32_t insn_size = 0; - - disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache); - - uint32_t num_insns = disasm_sp->GetInstructionList().GetSize(); - if (num_insns) { - prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0); - insn_size = prev_insn->GetOpcode().GetByteSize(); - if (i == 1 && insn_size == 2) { - // This looks like a valid 2-byte instruction (but it could be a part - // of upper 4 byte instruction). - instruction_list.Append(prev_insn); - inst_to_choose = 1; - } else if (i == 2) { - // Here we may get one 4-byte instruction or two 2-byte instructions. - if (num_insns == 2) { - // Looks like there are two 2-byte instructions above our - // breakpoint target address. Now the upper 2-byte instruction is - // either a valid 2-byte instruction or could be a part of it's - // upper 4-byte instruction. In both cases we don't care because in - // this case lower 2-byte instruction is definitely a valid - // instruction and whatever i=1 iteration has found out is true. - inst_to_choose = 1; - break; - } else if (insn_size == 4) { - // This instruction claims its a valid 4-byte instruction. But it - // could be a part of it's upper 4-byte instruction. Lets try - // scanning upper 2 bytes to verify this. - instruction_list.Append(prev_insn); - inst_to_choose = 2; - } - } else if (i == 3) { - if (insn_size == 4) - // FIXME: We reached here that means instruction at [target - 4] has - // already claimed to be a 4-byte instruction, and now instruction - // at [target - 6] is also claiming that it's a 4-byte instruction. - // This can not be true. In this case we can not decide the valid - // previous instruction so we let lldb set the breakpoint at the - // address given by user. - inst_to_choose = 0; - else - // This is straight-forward - inst_to_choose = 2; - break; - } - } else { - // Decode failed, bytes do not form a valid instruction. So whatever - // previous iteration has found out is true. - if (i > 1) { - inst_to_choose = i - 1; - break; - } - } - } - - // Check if we are able to find any valid instruction. - if (inst_to_choose) { - if (inst_to_choose > instruction_list.GetSize()) - inst_to_choose--; - prev_insn = instruction_list.GetInstructionAtIndex(inst_to_choose - 1); - - if (prev_insn->HasDelaySlot()) { - uint32_t shift_size = prev_insn->GetOpcode().GetByteSize(); - // Adjust the breakable address - breakable_addr = addr - shift_size; - if (log) - log->Printf("Target::%s Breakpoint at 0x%8.8" PRIx64 - " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n", - __FUNCTION__, addr, breakable_addr); - } - } - break; - } - } - return breakable_addr; + auto arch_plugin = GetArchitecturePlugin(); + return arch_plugin ? + arch_plugin->GetBreakableLoadAddress(addr, *this) : addr; } SourceManager &Target::GetSourceManager() { @@ -2971,18 +2834,7 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { PlatformSP platform_sp(GetPlatform()); - // Finalize the file actions, and if none were given, default to opening up a - // pseudo terminal - const bool default_to_use_pty = platform_sp ? platform_sp->IsHost() : false; - 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); + FinalizeFileActions(launch_info); if (state == eStateConnected) { if (launch_info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { @@ -3003,22 +2855,15 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { log->Printf("Target::%s asking the platform to debug the process", __FUNCTION__); - // Get a weak pointer to the previous process if we have one - ProcessWP process_wp; - if (m_process_sp) - process_wp = m_process_sp; + // If there was a previous process, delete it before we make the new one. + // One subtle point, we delete the process before we release the reference + // to m_process_sp. That way even if we are the last owner, the process + // will get Finalized before it gets destroyed. + DeleteCurrentProcess(); + m_process_sp = GetPlatform()->DebugProcess(launch_info, debugger, this, error); - // Cleanup the old process since someone might still have a strong - // reference to this process and we would like to allow it to cleanup as - // much as it can without the object being destroyed. We try to lock the - // shared pointer and if that works, then someone else still has a strong - // reference to the process. - - ProcessSP old_process_sp(process_wp.lock()); - if (old_process_sp) - old_process_sp->Finalize(); } else { if (log) log->Printf("Target::%s the platform doesn't know how to debug a " @@ -3030,8 +2875,7 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { } else { // Use a Process plugin to construct the process. const char *plugin_name = launch_info.GetProcessPluginName(); - CreateProcess(launch_info.GetListenerForProcess(debugger), plugin_name, - nullptr); + CreateProcess(launch_info.GetListener(), plugin_name, nullptr); } // Since we didn't have a platform launch the process, launch it here. @@ -3206,6 +3050,86 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { return error; } +void Target::FinalizeFileActions(ProcessLaunchInfo &info) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Finalize the file actions, and if none were given, default to opening up a + // pseudo terminal + PlatformSP platform_sp = GetPlatform(); + const bool default_to_use_pty = + m_platform_sp ? m_platform_sp->IsHost() : false; + LLDB_LOG( + log, + "have platform={0}, platform_sp->IsHost()={1}, default_to_use_pty={2}", + bool(platform_sp), + platform_sp ? (platform_sp->IsHost() ? "true" : "false") : "n/a", + default_to_use_pty); + + // 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 (info.GetFileActionForFD(STDIN_FILENO) == nullptr || + info.GetFileActionForFD(STDOUT_FILENO) == nullptr || + info.GetFileActionForFD(STDERR_FILENO) == nullptr) { + LLDB_LOG(log, "at least one of stdin/stdout/stderr was not set, evaluating " + "default handling"); + + if (info.GetFlags().Test(eLaunchFlagLaunchInTTY)) { + // Do nothing, if we are launching in a remote terminal no file actions + // should be done at all. + return; + } + + if (info.GetFlags().Test(eLaunchFlagDisableSTDIO)) { + LLDB_LOG(log, "eLaunchFlagDisableSTDIO set, adding suppression action " + "for stdin, stdout and stderr"); + info.AppendSuppressFileAction(STDIN_FILENO, true, false); + info.AppendSuppressFileAction(STDOUT_FILENO, false, true); + info.AppendSuppressFileAction(STDERR_FILENO, false, true); + } else { + // Check for any values that might have gotten set with any of: (lldb) + // settings set target.input-path (lldb) settings set target.output-path + // (lldb) settings set target.error-path + FileSpec in_file_spec; + FileSpec out_file_spec; + FileSpec err_file_spec; + // Only override with the target settings if we don't already have an + // action for in, out or error + if (info.GetFileActionForFD(STDIN_FILENO) == nullptr) + in_file_spec = GetStandardInputPath(); + if (info.GetFileActionForFD(STDOUT_FILENO) == nullptr) + out_file_spec = GetStandardOutputPath(); + if (info.GetFileActionForFD(STDERR_FILENO) == nullptr) + err_file_spec = GetStandardErrorPath(); + + LLDB_LOG(log, "target stdin='{0}', target stdout='{1}', stderr='{1}'", + in_file_spec, out_file_spec, err_file_spec); + + if (in_file_spec) { + info.AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false); + LLDB_LOG(log, "appended stdin open file action for {0}", in_file_spec); + } + + if (out_file_spec) { + info.AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true); + LLDB_LOG(log, "appended stdout open file action for {0}", + out_file_spec); + } + + if (err_file_spec) { + info.AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true); + LLDB_LOG(log, "appended stderr open file action for {0}", + err_file_spec); + } + + if (default_to_use_pty && + (!in_file_spec || !out_file_spec || !err_file_spec)) { + llvm::Error Err = info.SetUpPtyRedirection(); + LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}"); + } + } + } +} + //-------------------------------------------------------------- // Target::StopHook //-------------------------------------------------------------- @@ -3275,16 +3199,20 @@ void Target::StopHook::GetDescription(Stream *s, // class TargetProperties //-------------------------------------------------------------- -OptionEnumValueElement lldb_private::g_dynamic_value_types[] = { +// clang-format off +static constexpr OptionEnumValueElement g_dynamic_value_types[] = { {eNoDynamicValues, "no-dynamic-values", "Don't calculate the dynamic type of values"}, {eDynamicCanRunTarget, "run-target", "Calculate the dynamic type of values " "even if you have to run the target."}, {eDynamicDontRunTarget, "no-run-target", - "Calculate the dynamic type of values, but don't run the target."}, - {0, nullptr, nullptr}}; + "Calculate the dynamic type of values, but don't run the target."} }; + +OptionEnumValues lldb_private::GetDynamicValueTypes() { + return OptionEnumValues(g_dynamic_value_types); +} -static OptionEnumValueElement g_inline_breakpoint_enums[] = { +static constexpr OptionEnumValueElement g_inline_breakpoint_enums[] = { {eInlineBreakpointsNever, "never", "Never look for inline breakpoint " "locations (fastest). This setting " "should only be used if you know that " @@ -3295,8 +3223,7 @@ static OptionEnumValueElement g_inline_breakpoint_enums[] = { "files (default)."}, {eInlineBreakpointsAlways, "always", "Always look for inline breakpoint locations when setting file and line " - "breakpoints (slower but most accurate)."}, - {0, nullptr, nullptr}}; + "breakpoints (slower but most accurate)."} }; typedef enum x86DisassemblyFlavor { eX86DisFlavorDefault, @@ -3304,36 +3231,33 @@ typedef enum x86DisassemblyFlavor { eX86DisFlavorATT } x86DisassemblyFlavor; -static OptionEnumValueElement g_x86_dis_flavor_value_types[] = { +static constexpr OptionEnumValueElement g_x86_dis_flavor_value_types[] = { {eX86DisFlavorDefault, "default", "Disassembler default (currently att)."}, {eX86DisFlavorIntel, "intel", "Intel disassembler flavor."}, - {eX86DisFlavorATT, "att", "AT&T disassembler flavor."}, - {0, nullptr, nullptr}}; + {eX86DisFlavorATT, "att", "AT&T disassembler flavor."} }; -static OptionEnumValueElement g_hex_immediate_style_values[] = { +static constexpr OptionEnumValueElement g_hex_immediate_style_values[] = { {Disassembler::eHexStyleC, "c", "C-style (0xffff)."}, - {Disassembler::eHexStyleAsm, "asm", "Asm-style (0ffffh)."}, - {0, nullptr, nullptr}}; + {Disassembler::eHexStyleAsm, "asm", "Asm-style (0ffffh)."} }; -static OptionEnumValueElement g_load_script_from_sym_file_values[] = { +static constexpr OptionEnumValueElement g_load_script_from_sym_file_values[] = { {eLoadScriptFromSymFileTrue, "true", "Load debug scripts inside symbol files"}, {eLoadScriptFromSymFileFalse, "false", "Do not load debug scripts inside symbol files."}, {eLoadScriptFromSymFileWarn, "warn", - "Warn about debug scripts inside symbol files but do not load them."}, - {0, nullptr, nullptr}}; + "Warn about debug scripts inside symbol files but do not load them."} }; -static OptionEnumValueElement g_load_current_working_dir_lldbinit_values[] = { +static constexpr +OptionEnumValueElement g_load_current_working_dir_lldbinit_values[] = { {eLoadCWDlldbinitTrue, "true", "Load .lldbinit files from current directory"}, {eLoadCWDlldbinitFalse, "false", "Do not load .lldbinit files from current directory"}, {eLoadCWDlldbinitWarn, "warn", - "Warn about loading .lldbinit files from current directory"}, - {0, nullptr, nullptr}}; + "Warn about loading .lldbinit files from current directory"} }; -static OptionEnumValueElement g_memory_module_load_level_values[] = { +static constexpr OptionEnumValueElement g_memory_module_load_level_values[] = { {eMemoryModuleLoadLevelMinimal, "minimal", "Load minimal information when loading modules from memory. Currently " "this setting loads sections only."}, @@ -3342,28 +3266,27 @@ static OptionEnumValueElement g_memory_module_load_level_values[] = { "this setting loads sections and function bounds."}, {eMemoryModuleLoadLevelComplete, "complete", "Load complete information when loading modules from memory. Currently " - "this setting loads sections and all symbols."}, - {0, nullptr, nullptr}}; + "this setting loads sections and all symbols."} }; -static PropertyDefinition g_properties[] = { - {"default-arch", OptionValue::eTypeArch, true, 0, nullptr, nullptr, +static constexpr PropertyDefinition g_properties[] = { + {"default-arch", OptionValue::eTypeArch, true, 0, nullptr, {}, "Default architecture to choose, when there's a choice."}, {"move-to-nearest-code", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "Move breakpoints to nearest code."}, + {}, "Move breakpoints to nearest code."}, {"language", OptionValue::eTypeLanguage, false, eLanguageTypeUnknown, - nullptr, nullptr, + nullptr, {}, "The language to use when interpreting expressions entered in commands."}, - {"expr-prefix", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, + {"expr-prefix", OptionValue::eTypeFileSpec, false, 0, nullptr, {}, "Path to a file containing expressions to be prepended to all " "expressions."}, {"prefer-dynamic-value", OptionValue::eTypeEnum, false, - eDynamicDontRunTarget, nullptr, g_dynamic_value_types, + eDynamicDontRunTarget, nullptr, OptionEnumValues(g_dynamic_value_types), "Should printed values be shown as their dynamic value."}, {"enable-synthetic-value", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "Should synthetic values be used by default whenever available."}, - {"skip-prologue", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, + {}, "Should synthetic values be used by default whenever available."}, + {"skip-prologue", OptionValue::eTypeBoolean, false, true, nullptr, {}, "Skip function prologues when setting breakpoints by name."}, - {"source-map", OptionValue::eTypePathMap, false, 0, nullptr, nullptr, + {"source-map", OptionValue::eTypePathMap, false, 0, nullptr, {}, "Source path remappings are used to track the change of location between " "a source file when built, and " "where it exists on the current system. It consists of an array of " @@ -3375,66 +3298,68 @@ static PropertyDefinition g_properties[] = { "Each element of the array is checked in order and the first one that " "results in a match wins."}, {"exec-search-paths", OptionValue::eTypeFileSpecList, false, 0, nullptr, - nullptr, "Executable search paths to use when locating executable files " - "whose paths don't match the local file system."}, + {}, "Executable search paths to use when locating executable files " + "whose paths don't match the local file system."}, {"debug-file-search-paths", OptionValue::eTypeFileSpecList, false, 0, - nullptr, nullptr, - "List of directories to be searched when locating debug symbol files."}, + nullptr, {}, + "List of directories to be searched when locating debug symbol files. " + "See also symbols.enable-external-lookup."}, {"clang-module-search-paths", OptionValue::eTypeFileSpecList, false, 0, - nullptr, nullptr, + nullptr, {}, "List of directories to be searched when locating modules for Clang."}, {"auto-import-clang-modules", OptionValue::eTypeBoolean, false, true, - nullptr, nullptr, + nullptr, {}, "Automatically load Clang modules referred to by the program."}, {"auto-apply-fixits", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "Automatically apply fix-it hints to expressions."}, + {}, "Automatically apply fix-it hints to expressions."}, {"notify-about-fixits", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "Print the fixed expression text."}, + {}, "Print the fixed expression text."}, {"save-jit-objects", OptionValue::eTypeBoolean, false, false, nullptr, - nullptr, "Save intermediate object files generated by the LLVM JIT"}, + {}, "Save intermediate object files generated by the LLVM JIT"}, {"max-children-count", OptionValue::eTypeSInt64, false, 256, nullptr, - nullptr, "Maximum number of children to expand in any level of depth."}, + {}, "Maximum number of children to expand in any level of depth."}, {"max-string-summary-length", OptionValue::eTypeSInt64, false, 1024, - nullptr, nullptr, + nullptr, {}, "Maximum number of characters to show when using %s in summary strings."}, {"max-memory-read-size", OptionValue::eTypeSInt64, false, 1024, nullptr, - nullptr, "Maximum number of bytes that 'memory read' will fetch before " - "--force must be specified."}, + {}, "Maximum number of bytes that 'memory read' will fetch before " + "--force must be specified."}, {"breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean, false, - true, nullptr, nullptr, "Consult the platform module avoid list when " - "setting non-module specific breakpoints."}, - {"arg0", OptionValue::eTypeString, false, 0, nullptr, nullptr, + true, nullptr, {}, "Consult the platform module avoid list when " + "setting non-module specific breakpoints."}, + {"arg0", OptionValue::eTypeString, false, 0, nullptr, {}, "The first argument passed to the program in the argument array which can " "be different from the executable itself."}, - {"run-args", OptionValue::eTypeArgs, false, 0, nullptr, nullptr, + {"run-args", OptionValue::eTypeArgs, false, 0, nullptr, {}, "A list containing all the arguments to be passed to the executable when " "it is run. Note that this does NOT include the argv[0] which is in " "target.arg0."}, {"env-vars", OptionValue::eTypeDictionary, false, OptionValue::eTypeString, - nullptr, nullptr, "A list of all the environment variables to be passed " - "to the executable's environment, and their values."}, - {"inherit-env", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, + nullptr, {}, "A list of all the environment variables to be passed " + "to the executable's environment, and their values."}, + {"inherit-env", OptionValue::eTypeBoolean, false, true, nullptr, {}, "Inherit the environment from the process that is running LLDB."}, - {"input-path", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, + {"input-path", OptionValue::eTypeFileSpec, false, 0, nullptr, {}, "The file/path to be used by the executable program for reading its " "standard input."}, - {"output-path", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, + {"output-path", OptionValue::eTypeFileSpec, false, 0, nullptr, {}, "The file/path to be used by the executable program for writing its " "standard output."}, - {"error-path", OptionValue::eTypeFileSpec, false, 0, nullptr, nullptr, + {"error-path", OptionValue::eTypeFileSpec, false, 0, nullptr, {}, "The file/path to be used by the executable program for writing its " "standard error."}, {"detach-on-error", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "debugserver will detach (rather than killing) a process if it " + {}, "debugserver will detach (rather than killing) a process if it " "loses connection with lldb."}, - {"preload-symbols", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, + {"preload-symbols", OptionValue::eTypeBoolean, false, true, nullptr, {}, "Enable loading of symbol tables before they are needed."}, - {"disable-aslr", OptionValue::eTypeBoolean, false, true, nullptr, nullptr, + {"disable-aslr", OptionValue::eTypeBoolean, false, true, nullptr, {}, "Disable Address Space Layout Randomization (ASLR)"}, - {"disable-stdio", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, + {"disable-stdio", OptionValue::eTypeBoolean, false, false, nullptr, {}, "Disable stdin/stdout for process (e.g. for a GUI application)"}, {"inline-breakpoint-strategy", OptionValue::eTypeEnum, false, - eInlineBreakpointsAlways, nullptr, g_inline_breakpoint_enums, + eInlineBreakpointsAlways, nullptr, + OptionEnumValues(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 " @@ -3454,25 +3379,29 @@ static PropertyDefinition g_properties[] = { // 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, nullptr, g_x86_dis_flavor_value_types, + eX86DisFlavorDefault, nullptr, + OptionEnumValues(g_x86_dis_flavor_value_types), "The default disassembly flavor to use for x86 or x86-64 targets."}, {"use-hex-immediates", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "Show immediates in disassembly as hexadecimal."}, + {}, "Show immediates in disassembly as hexadecimal."}, {"hex-immediate-style", OptionValue::eTypeEnum, false, - Disassembler::eHexStyleC, nullptr, g_hex_immediate_style_values, + Disassembler::eHexStyleC, nullptr, + OptionEnumValues(g_hex_immediate_style_values), "Which style to use for printing hexadecimal disassembly values."}, {"use-fast-stepping", OptionValue::eTypeBoolean, false, true, nullptr, - nullptr, "Use a fast stepping algorithm based on running from branch to " - "branch rather than instruction single-stepping."}, + {}, "Use a fast stepping algorithm based on running from branch to " + "branch rather than instruction single-stepping."}, {"load-script-from-symbol-file", OptionValue::eTypeEnum, false, - eLoadScriptFromSymFileWarn, nullptr, g_load_script_from_sym_file_values, + eLoadScriptFromSymFileWarn, nullptr, + OptionEnumValues(g_load_script_from_sym_file_values), "Allow LLDB to load scripting resources embedded in symbol files when " "available."}, {"load-cwd-lldbinit", OptionValue::eTypeEnum, false, eLoadCWDlldbinitWarn, - nullptr, g_load_current_working_dir_lldbinit_values, + nullptr, OptionEnumValues(g_load_current_working_dir_lldbinit_values), "Allow LLDB to .lldbinit files from the current directory automatically."}, {"memory-module-load-level", OptionValue::eTypeEnum, false, - eMemoryModuleLoadLevelComplete, nullptr, g_memory_module_load_level_values, + eMemoryModuleLoadLevelComplete, nullptr, + OptionEnumValues(g_memory_module_load_level_values), "Loading modules from memory can be slow as reading the symbol tables and " "other data can take a long time depending on your connection to the " "debug target. " @@ -3488,20 +3417,23 @@ static PropertyDefinition g_properties[] = { "symbols, but should rarely be used as stack frames in these memory " "regions will be inaccurate and not provide any context (fastest). "}, {"display-expression-in-crashlogs", OptionValue::eTypeBoolean, false, false, - nullptr, nullptr, "Expressions that crash will show up in crash logs if " - "the host system supports executable specific crash log " - "strings and this setting is set to true."}, + nullptr, {}, "Expressions that crash will show up in crash logs if " + "the host system supports executable specific crash log " + "strings and this setting is set to true."}, {"trap-handler-names", OptionValue::eTypeArray, true, - OptionValue::eTypeString, nullptr, nullptr, + OptionValue::eTypeString, nullptr, {}, "A list of trap handler function names, e.g. a common Unix user process " "one is _sigtramp."}, {"display-runtime-support-values", OptionValue::eTypeBoolean, false, false, - nullptr, nullptr, "If true, LLDB will show variables that are meant to " - "support the operation of a language's runtime " - "support."}, - {"non-stop-mode", OptionValue::eTypeBoolean, false, 0, nullptr, nullptr, + nullptr, {}, "If true, LLDB will show variables that are meant to " + "support the operation of a language's runtime support."}, + {"display-recognized-arguments", OptionValue::eTypeBoolean, false, false, + nullptr, {}, "Show recognized arguments in variable listings by default."}, + {"non-stop-mode", OptionValue::eTypeBoolean, false, 0, nullptr, {}, "Disable lock-step debugging, instead control threads independently."}, - {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; + {"require-hardware-breakpoint", OptionValue::eTypeBoolean, false, 0, + nullptr, {}, "Require all breakpoints to be hardware breakpoints."}}; +// clang-format on enum { ePropertyDefaultArch, @@ -3545,8 +3477,10 @@ enum { ePropertyDisplayExpressionsInCrashlogs, ePropertyTrapHandlerNames, ePropertyDisplayRuntimeSupportValues, + ePropertyDisplayRecognizedArguments, ePropertyNonStopModeEnabled, - ePropertyExperimental + ePropertyRequireHardwareBreakpoints, + ePropertyExperimental, }; class TargetOptionValueProperties : public OptionValueProperties { @@ -3623,16 +3557,15 @@ protected: //---------------------------------------------------------------------- // TargetProperties //---------------------------------------------------------------------- -static PropertyDefinition g_experimental_properties[]{ +static constexpr PropertyDefinition g_experimental_properties[]{ {"inject-local-vars", OptionValue::eTypeBoolean, true, true, nullptr, - nullptr, + {}, "If true, inject local variables explicitly into the expression text. " "This will fix symbol resolution when there are name collisions between " "ivars and local variables. " "But it can make expressions run much more slowly."}, {"use-modern-type-lookup", OptionValue::eTypeBoolean, true, false, nullptr, - nullptr, "If true, use Clang's modern type lookup infrastructure."}, - {nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}}; + {}, "If true, use Clang's modern type lookup infrastructure."}}; enum { ePropertyInjectLocalVars = 0, ePropertyUseModernTypeLookup }; @@ -4104,6 +4037,16 @@ void TargetProperties::SetDisplayRuntimeSupportValues(bool b) { m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } +bool TargetProperties::GetDisplayRecognizedArguments() const { + const uint32_t idx = ePropertyDisplayRecognizedArguments; + return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); +} + +void TargetProperties::SetDisplayRecognizedArguments(bool b) { + const uint32_t idx = ePropertyDisplayRecognizedArguments; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + bool TargetProperties::GetNonStopModeEnabled() const { const uint32_t idx = ePropertyNonStopModeEnabled; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); @@ -4145,6 +4088,17 @@ void TargetProperties::SetProcessLaunchInfo( SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO)); } +bool TargetProperties::GetRequireHardwareBreakpoints() const { + const uint32_t idx = ePropertyRequireHardwareBreakpoints; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetRequireHardwareBreakpoints(bool b) { + const uint32_t idx = ePropertyRequireHardwareBreakpoints; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); +} + void TargetProperties::Arg0ValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = diff --git a/source/Target/TargetList.cpp b/source/Target/TargetList.cpp index b9c852b414e2..5b262509cae7 100644 --- a/source/Target/TargetList.cpp +++ b/source/Target/TargetList.cpp @@ -7,14 +7,10 @@ // //===----------------------------------------------------------------------===// -// Project includes #include "lldb/Target/TargetList.h" -#include "lldb/Core/Broadcaster.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/Event.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" -#include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -22,10 +18,12 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/State.h" #include "lldb/Utility/TildeExpressionResolver.h" #include "lldb/Utility/Timer.h" -// Other libraries and framework includes #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" @@ -58,27 +56,27 @@ TargetList::~TargetList() { Status TargetList::CreateTarget(Debugger &debugger, llvm::StringRef user_exe_path, llvm::StringRef triple_str, - bool get_dependent_files, + LoadDependentFiles load_dependent_files, const OptionGroupPlatform *platform_options, TargetSP &target_sp) { return CreateTargetInternal(debugger, user_exe_path, triple_str, - get_dependent_files, platform_options, target_sp, + load_dependent_files, platform_options, target_sp, false); } Status TargetList::CreateTarget(Debugger &debugger, llvm::StringRef user_exe_path, const ArchSpec &specified_arch, - bool get_dependent_files, + LoadDependentFiles load_dependent_files, PlatformSP &platform_sp, TargetSP &target_sp) { return CreateTargetInternal(debugger, user_exe_path, specified_arch, - get_dependent_files, platform_sp, target_sp, + load_dependent_files, platform_sp, target_sp, false); } Status TargetList::CreateTargetInternal( Debugger &debugger, llvm::StringRef user_exe_path, - llvm::StringRef triple_str, bool get_dependent_files, + llvm::StringRef triple_str, LoadDependentFiles load_dependent_files, const OptionGroupPlatform *platform_options, TargetSP &target_sp, bool is_dummy_target) { Status error; @@ -120,8 +118,8 @@ Status TargetList::CreateTargetInternal( if (!user_exe_path.empty()) { ModuleSpecList module_specs; ModuleSpec module_spec; - module_spec.GetFileSpec().SetFile(user_exe_path, true, - FileSpec::Style::native); + module_spec.GetFileSpec().SetFile(user_exe_path, FileSpec::Style::native); + FileSystem::Instance().Resolve(module_spec.GetFileSpec()); // Resolve the executable in case we are given a path to a application // bundle like a .app bundle on MacOSX @@ -236,7 +234,7 @@ Status TargetList::CreateTargetInternal( // All platforms for all modules in the executable match, so we can // select this platform platform_sp = platforms.front(); - } else if (more_than_one_platforms == false) { + } else if (!more_than_one_platforms) { // No platforms claim to support this file error.SetErrorString("No matching platforms found for this file, " "specify one with the --platform option"); @@ -292,7 +290,7 @@ Status TargetList::CreateTargetInternal( platform_arch = arch; error = TargetList::CreateTargetInternal( - debugger, user_exe_path, platform_arch, get_dependent_files, platform_sp, + debugger, user_exe_path, platform_arch, load_dependent_files, platform_sp, target_sp, is_dummy_target); return error; } @@ -315,14 +313,14 @@ Status TargetList::CreateDummyTarget(Debugger &debugger, lldb::TargetSP &target_sp) { PlatformSP host_platform_sp(Platform::GetHostPlatform()); return CreateTargetInternal( - debugger, (const char *)nullptr, specified_arch_name, false, + debugger, (const char *)nullptr, specified_arch_name, eLoadDependentsNo, (const OptionGroupPlatform *)nullptr, target_sp, true); } Status TargetList::CreateTargetInternal(Debugger &debugger, llvm::StringRef user_exe_path, const ArchSpec &specified_arch, - bool get_dependent_files, + LoadDependentFiles load_dependent_files, lldb::PlatformSP &platform_sp, lldb::TargetSP &target_sp, bool is_dummy_target) { @@ -346,8 +344,8 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, if (!arch.IsValid()) arch = specified_arch; - FileSpec file(user_exe_path, false); - if (!file.Exists() && user_exe_path.startswith("~")) { + FileSpec file(user_exe_path); + if (!FileSystem::Instance().Exists(file) && user_exe_path.startswith("~")) { // we want to expand the tilde but we don't want to resolve any symbolic // links so we can't use the FileSpec constructor's resolve flag llvm::SmallString<64> unglobbed_path; @@ -355,24 +353,24 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, Resolver.ResolveFullPath(user_exe_path, unglobbed_path); if (unglobbed_path.empty()) - file = FileSpec(user_exe_path, false); + file = FileSpec(user_exe_path); else - file = FileSpec(unglobbed_path.c_str(), false); + file = FileSpec(unglobbed_path.c_str()); } bool user_exe_path_is_bundle = false; char resolved_bundle_exe_path[PATH_MAX]; resolved_bundle_exe_path[0] = '\0'; if (file) { - if (llvm::sys::fs::is_directory(file.GetPath())) + if (FileSystem::Instance().IsDirectory(file)) user_exe_path_is_bundle = true; if (file.IsRelative() && !user_exe_path.empty()) { llvm::SmallString<64> cwd; if (! llvm::sys::fs::current_path(cwd)) { - FileSpec cwd_file(cwd.c_str(), false); + FileSpec cwd_file(cwd.c_str()); cwd_file.AppendPathComponent(file); - if (cwd_file.Exists()) + if (FileSystem::Instance().Exists(cwd_file)) file = cwd_file; } } @@ -401,7 +399,7 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, return error; } target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); - target_sp->SetExecutableModule(exe_module_sp, get_dependent_files); + target_sp->SetExecutableModule(exe_module_sp, load_dependent_files); if (user_exe_path_is_bundle) exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, sizeof(resolved_bundle_exe_path)); diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp index 5ac1de7ae01b..569d7a0a0f9e 100644 --- a/source/Target/Thread.cpp +++ b/source/Target/Thread.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/Thread.h" #include "Plugins/Process/Utility/UnwindLLDB.h" #include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" @@ -18,7 +14,6 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Module.h" -#include "lldb/Core/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" @@ -28,8 +23,10 @@ #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" @@ -49,6 +46,7 @@ #include "lldb/Target/Unwind.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" #include "lldb/lldb-enumerations.h" @@ -64,30 +62,31 @@ const ThreadPropertiesSP &Thread::GetGlobalProperties() { return *g_settings_sp_ptr; } -static PropertyDefinition g_properties[] = { +static constexpr PropertyDefinition g_properties[] = { {"step-in-avoid-nodebug", OptionValue::eTypeBoolean, true, true, nullptr, - nullptr, + {}, "If true, step-in will not stop in functions with no debug information."}, {"step-out-avoid-nodebug", OptionValue::eTypeBoolean, true, false, nullptr, - nullptr, "If true, when step-in/step-out/step-over leave the current " - "frame, they will continue to step out till they come to a " - "function with " - "debug information. Passing a frame argument to step-out will " - "override this option."}, - {"step-avoid-regexp", OptionValue::eTypeRegex, true, 0, "^std::", nullptr, + {}, "If true, when step-in/step-out/step-over leave the current frame, " + "they will continue to step out till they come to a function with " + "debug information. Passing a frame argument to step-out will " + "override this option."}, + {"step-avoid-regexp", OptionValue::eTypeRegex, true, 0, "^std::", {}, "A regular expression defining functions step-in won't stop in."}, {"step-avoid-libraries", OptionValue::eTypeFileSpecList, true, 0, nullptr, - nullptr, "A list of libraries that source stepping won't stop in."}, - {"trace-thread", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, + {}, "A list of libraries that source stepping won't stop in."}, + {"trace-thread", OptionValue::eTypeBoolean, false, false, nullptr, {}, "If true, this thread will single-step and log execution."}, - {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; + {"max-backtrace-depth", OptionValue::eTypeUInt64, false, 300000, nullptr, + {}, "Maximum number of frames to backtrace."}}; enum { ePropertyStepInAvoidsNoDebug, ePropertyStepOutAvoidsNoDebug, ePropertyStepAvoidRegex, ePropertyStepAvoidLibraries, - ePropertyEnableThreadTrace + ePropertyEnableThreadTrace, + ePropertyMaxBacktraceDepth }; class ThreadOptionValueProperties : public OptionValueProperties { @@ -165,6 +164,12 @@ bool ThreadProperties::GetStepOutAvoidsNoDebug() const { nullptr, idx, g_properties[idx].default_uint_value != 0); } +uint64_t ThreadProperties::GetMaxBacktraceDepth() const { + const uint32_t idx = ePropertyMaxBacktraceDepth; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_properties[idx].default_uint_value != 0); +} + //------------------------------------------------------------------ // Thread Event Data //------------------------------------------------------------------ @@ -259,6 +264,7 @@ Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id) static_cast<void *>(this), GetID()); CheckInWithManager(); + QueueFundamentalPlan(true); } @@ -392,13 +398,14 @@ lldb::StopInfoSP Thread::GetStopInfo() { m_stop_info_sp ->IsValid() && m_stop_info_stop_id == stop_id; bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded(); + bool plan_failed = completed_plan_sp && !completed_plan_sp->PlanSucceeded(); bool plan_overrides_trace = have_valid_stop_info && have_valid_completed_plan && (m_stop_info_sp->GetStopReason() == eStopReasonTrace); - if (have_valid_stop_info && !plan_overrides_trace) { + if (have_valid_stop_info && !plan_overrides_trace && !plan_failed) { return m_stop_info_sp; - } else if (have_valid_completed_plan) { + } else if (completed_plan_sp) { return StopInfo::CreateStopReasonWithPlan( completed_plan_sp, GetReturnValueObject(), GetExpressionVariable()); } else { @@ -1031,9 +1038,11 @@ void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) { if (thread_plan_sp) { // If the thread plan doesn't already have a tracer, give it its parent's // tracer: - if (!thread_plan_sp->GetThreadPlanTracer()) + if (!thread_plan_sp->GetThreadPlanTracer()) { + assert(!m_plan_stack.empty()); thread_plan_sp->SetThreadPlanTracer( m_plan_stack.back()->GetThreadPlanTracer()); + } m_plan_stack.push_back(thread_plan_sp); thread_plan_sp->DidPush(); @@ -1170,12 +1179,34 @@ ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) { return nullptr; } -void Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp, - bool abort_other_plans) { +Status Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp, + bool abort_other_plans) { + Status status; + StreamString s; + if (!thread_plan_sp->ValidatePlan(&s)) { + DiscardThreadPlansUpToPlan(thread_plan_sp); + thread_plan_sp.reset(); + status.SetErrorString(s.GetString()); + return status; + } + if (abort_other_plans) DiscardThreadPlans(true); PushPlan(thread_plan_sp); + + // 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(&s)) { + DiscardThreadPlansUpToPlan(thread_plan_sp); + thread_plan_sp.reset(); + status.SetErrorString(s.GetString()); + return status; + } + + return status; } void Thread::EnableTracer(bool value, bool single_stepping) { @@ -1340,23 +1371,24 @@ ThreadPlanSP Thread::QueueFundamentalPlan(bool abort_other_plans) { } ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction( - bool step_over, bool abort_other_plans, bool stop_other_threads) { + bool step_over, bool abort_other_plans, bool stop_other_threads, + Status &status) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepInstruction( *this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion)); - QueueThreadPlan(thread_plan_sp, abort_other_plans); + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, - LazyBool step_out_avoids_code_withoug_debug_info) { + Status &status, LazyBool step_out_avoids_code_withoug_debug_info) { ThreadPlanSP thread_plan_sp; thread_plan_sp.reset(new ThreadPlanStepOverRange( *this, range, addr_context, stop_other_threads, step_out_avoids_code_withoug_debug_info)); - QueueThreadPlan(thread_plan_sp, abort_other_plans); + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } @@ -1365,17 +1397,17 @@ ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, - LazyBool step_out_avoids_code_withoug_debug_info) { + Status &status, LazyBool step_out_avoids_code_withoug_debug_info) { return QueueThreadPlanForStepOverRange( abort_other_plans, line_entry.GetSameLineContiguousAddressRange(), - addr_context, stop_other_threads, + addr_context, stop_other_threads, status, step_out_avoids_code_withoug_debug_info); } ThreadPlanSP Thread::QueueThreadPlanForStepInRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, const char *step_in_target, - lldb::RunMode stop_other_threads, + lldb::RunMode stop_other_threads, Status &status, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp( @@ -1388,7 +1420,7 @@ ThreadPlanSP Thread::QueueThreadPlanForStepInRange( if (step_in_target) plan->SetStepInTarget(step_in_target); - QueueThreadPlan(thread_plan_sp, abort_other_plans); + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } @@ -1396,12 +1428,12 @@ ThreadPlanSP Thread::QueueThreadPlanForStepInRange( ThreadPlanSP Thread::QueueThreadPlanForStepInRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, const char *step_in_target, - lldb::RunMode stop_other_threads, + lldb::RunMode stop_other_threads, Status &status, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { return QueueThreadPlanForStepInRange( abort_other_plans, line_entry.GetSameLineContiguousAddressRange(), - addr_context, step_in_target, stop_other_threads, + addr_context, step_in_target, stop_other_threads, status, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } @@ -1409,23 +1441,19 @@ ThreadPlanSP Thread::QueueThreadPlanForStepInRange( ThreadPlanSP Thread::QueueThreadPlanForStepOut( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, - LazyBool step_out_avoids_code_without_debug_info) { + Status &status, LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, step_out_avoids_code_without_debug_info)); - if (thread_plan_sp->ValidatePlan(nullptr)) { - QueueThreadPlan(thread_plan_sp, abort_other_plans); - return thread_plan_sp; - } else { - return ThreadPlanSP(); - } + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, - bool continue_to_next_branch) { + Status &status, bool continue_to_next_branch) { const bool calculate_return_value = false; // No need to calculate the return value here. ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( @@ -1436,59 +1464,51 @@ ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop( static_cast<ThreadPlanStepOut *>(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); - if (thread_plan_sp->ValidatePlan(nullptr)) { - QueueThreadPlan(thread_plan_sp, abort_other_plans); - return thread_plan_sp; - } else { - return ThreadPlanSP(); - } + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepThrough(StackID &return_stack_id, bool abort_other_plans, - bool stop_other_threads) { + bool stop_other_threads, + Status &status) { ThreadPlanSP thread_plan_sp( new ThreadPlanStepThrough(*this, return_stack_id, stop_other_threads)); if (!thread_plan_sp || !thread_plan_sp->ValidatePlan(nullptr)) return ThreadPlanSP(); - QueueThreadPlan(thread_plan_sp, abort_other_plans); + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForRunToAddress(bool abort_other_plans, Address &target_addr, - bool stop_other_threads) { + bool stop_other_threads, + Status &status) { ThreadPlanSP thread_plan_sp( new ThreadPlanRunToAddress(*this, target_addr, stop_other_threads)); - QueueThreadPlan(thread_plan_sp, abort_other_plans); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } -ThreadPlanSP Thread::QueueThreadPlanForStepUntil(bool abort_other_plans, - lldb::addr_t *address_list, - size_t num_addresses, - bool stop_other_threads, - uint32_t frame_idx) { +ThreadPlanSP Thread::QueueThreadPlanForStepUntil( + bool abort_other_plans, lldb::addr_t *address_list, size_t num_addresses, + bool stop_other_threads, uint32_t frame_idx, Status &status) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepUntil( *this, address_list, num_addresses, stop_other_threads, frame_idx)); - QueueThreadPlan(thread_plan_sp, abort_other_plans); + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted( - bool abort_other_plans, const char *class_name, bool stop_other_threads) { + bool abort_other_plans, const char *class_name, bool stop_other_threads, + Status &status) { 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; + + status = QueueThreadPlan(thread_plan_sp, abort_other_plans); + return thread_plan_sp; } uint32_t Thread::GetIndexID() const { return m_index_id; } @@ -2107,12 +2127,12 @@ Status Thread::StepIn(bool source_step, if (source_step && frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = QueueThreadPlanForStepInRange( - abort_other_plans, sc.line_entry, sc, nullptr, run_mode, + abort_other_plans, sc.line_entry, sc, nullptr, run_mode, error, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } else { new_plan_sp = QueueThreadPlanForStepSingleInstruction( - false, abort_other_plans, run_mode); + false, abort_other_plans, run_mode, error); } new_plan_sp->SetIsMasterPlan(true); @@ -2141,11 +2161,11 @@ Status Thread::StepOver(bool source_step, if (source_step && frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = QueueThreadPlanForStepOverRange( - abort_other_plans, sc.line_entry, sc, run_mode, + abort_other_plans, sc.line_entry, sc, run_mode, error, step_out_avoids_code_without_debug_info); } else { new_plan_sp = QueueThreadPlanForStepSingleInstruction( - true, abort_other_plans, run_mode); + true, abort_other_plans, run_mode, error); } new_plan_sp->SetIsMasterPlan(true); @@ -2170,7 +2190,7 @@ Status Thread::StepOut() { ThreadPlanSP new_plan_sp(QueueThreadPlanForStepOut( abort_other_plans, nullptr, first_instruction, stop_other_threads, - eVoteYes, eVoteNoOpinion, 0)); + eVoteYes, eVoteNoOpinion, 0, error)); new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); @@ -2183,3 +2203,32 @@ Status Thread::StepOut() { } return error; } + +ValueObjectSP Thread::GetCurrentException() { + if (auto frame_sp = GetStackFrameAtIndex(0)) + if (auto recognized_frame = frame_sp->GetRecognizedFrame()) + if (auto e = recognized_frame->GetExceptionObject()) + return e; + + // FIXME: For now, only ObjC exceptions are supported. This should really + // iterate over all language runtimes and ask them all to give us the current + // exception. + if (auto runtime = GetProcess()->GetObjCLanguageRuntime()) + if (auto e = runtime->GetExceptionObjectForThread(shared_from_this())) + return e; + + return ValueObjectSP(); +} + +ThreadSP Thread::GetCurrentExceptionBacktrace() { + ValueObjectSP exception = GetCurrentException(); + if (!exception) return ThreadSP(); + + // FIXME: For now, only ObjC exceptions are supported. This should really + // iterate over all language runtimes and ask them all to give us the current + // exception. + auto runtime = GetProcess()->GetObjCLanguageRuntime(); + if (!runtime) return ThreadSP(); + + return runtime->GetBacktraceThreadFromException(exception); +} diff --git a/source/Target/ThreadList.cpp b/source/Target/ThreadList.cpp index ee57a401f742..20c285963882 100644 --- a/source/Target/ThreadList.cpp +++ b/source/Target/ThreadList.cpp @@ -7,15 +7,10 @@ // //===----------------------------------------------------------------------===// -// C Includes #include <stdlib.h> -// C++ Includes #include <algorithm> -// Other libraries and framework includes -// Project includes -#include "lldb/Core/State.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" @@ -23,6 +18,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Target/ThreadPlan.cpp b/source/Target/ThreadPlan.cpp index 31ee1e922494..1f2c69c6e8a6 100644 --- a/source/Target/ThreadPlan.cpp +++ b/source/Target/ThreadPlan.cpp @@ -7,18 +7,14 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlan.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/State.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" using namespace lldb; using namespace lldb_private; @@ -29,6 +25,7 @@ using namespace lldb_private; ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread, Vote stop_vote, Vote run_vote) : m_thread(thread), m_stop_vote(stop_vote), m_run_vote(run_vote), + m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false), m_kind(kind), m_name(name), m_plan_complete_mutex(), m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false), m_plan_private(false), m_okay_to_discard(true), m_is_master_plan(false), diff --git a/source/Target/ThreadPlanBase.cpp b/source/Target/ThreadPlanBase.cpp index 65b114584177..058d1468e241 100644 --- a/source/Target/ThreadPlanBase.cpp +++ b/source/Target/ThreadPlanBase.cpp @@ -9,10 +9,6 @@ #include "lldb/Target/ThreadPlanBase.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes // #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" @@ -61,10 +57,7 @@ bool ThreadPlanBase::ValidatePlan(Stream *error) { return true; } bool ThreadPlanBase::DoPlanExplainsStop(Event *event_ptr) { // The base plan should defer to its tracer, since by default it always // handles the stop. - if (TracerExplainsStop()) - return false; - else - return true; + return !TracerExplainsStop(); } Vote ThreadPlanBase::ShouldReportStop(Event *event_ptr) { diff --git a/source/Target/ThreadPlanCallFunction.cpp b/source/Target/ThreadPlanCallFunction.cpp index 18beda42fb64..1131ec939b89 100644 --- a/source/Target/ThreadPlanCallFunction.cpp +++ b/source/Target/ThreadPlanCallFunction.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" diff --git a/source/Target/ThreadPlanCallFunctionUsingABI.cpp b/source/Target/ThreadPlanCallFunctionUsingABI.cpp index b90fd9edd766..08604d2c411a 100644 --- a/source/Target/ThreadPlanCallFunctionUsingABI.cpp +++ b/source/Target/ThreadPlanCallFunctionUsingABI.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanCallFunctionUsingABI.h" #include "lldb/Core/Address.h" #include "lldb/Target/Process.h" diff --git a/source/Target/ThreadPlanCallOnFunctionExit.cpp b/source/Target/ThreadPlanCallOnFunctionExit.cpp index e8ea73f3c6a0..2ea083dac45e 100644 --- a/source/Target/ThreadPlanCallOnFunctionExit.cpp +++ b/source/Target/ThreadPlanCallOnFunctionExit.cpp @@ -27,17 +27,18 @@ void ThreadPlanCallOnFunctionExit::DidPush() { // completes. // Set stop vote to eVoteNo. + Status status; m_step_out_threadplan_sp = GetThread().QueueThreadPlanForStepOut( false, // abort other plans nullptr, // addr_context true, // first instruction true, // stop other threads eVoteNo, // do not say "we're stopping" - eVoteNoOpinion, // don't care about - // run state broadcasting + eVoteNoOpinion, // don't care about run state broadcasting 0, // frame_idx + status, // status eLazyBoolCalculate // avoid code w/o debinfo - ); + ); } // ------------------------------------------------------------------------- diff --git a/source/Target/ThreadPlanCallUserExpression.cpp b/source/Target/ThreadPlanCallUserExpression.cpp index 7bf0dd39993e..1fbd346feeea 100644 --- a/source/Target/ThreadPlanCallUserExpression.cpp +++ b/source/Target/ThreadPlanCallUserExpression.cpp @@ -9,11 +9,7 @@ #include "lldb/Target/ThreadPlanCallUserExpression.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Address.h" diff --git a/source/Target/ThreadPlanPython.cpp b/source/Target/ThreadPlanPython.cpp index 7796b8a0ab20..84b93bdc6583 100644 --- a/source/Target/ThreadPlanPython.cpp +++ b/source/Target/ThreadPlanPython.cpp @@ -9,12 +9,7 @@ #include "lldb/Target/ThreadPlan.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Core/Debugger.h" -#include "lldb/Core/State.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/Process.h" @@ -24,6 +19,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanPython.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" using namespace lldb; using namespace lldb_private; @@ -35,7 +31,7 @@ using namespace lldb_private; ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name) : ThreadPlan(ThreadPlan::eKindPython, "Python based Thread Plan", thread, eVoteNoOpinion, eVoteNoOpinion), - m_class_name(class_name) { + m_class_name(class_name), m_did_push(false) { SetIsMasterPlan(true); SetOkayToDiscard(true); SetPrivate(false); @@ -47,20 +43,22 @@ ThreadPlanPython::~ThreadPlanPython() { } 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) + if (!m_did_push) return true; - else + + if (!m_implementation_sp) { + if (error) + error->Printf("Python thread plan does not have an implementation"); return false; + } + + return true; } 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. - + m_did_push = true; if (!m_class_name.empty()) { ScriptInterpreter *script_interp = m_thread.GetProcess() ->GetTarget() diff --git a/source/Target/ThreadPlanRunToAddress.cpp b/source/Target/ThreadPlanRunToAddress.cpp index 6d1a8b5c27ff..bd11f8b82f78 100644 --- a/source/Target/ThreadPlanRunToAddress.cpp +++ b/source/Target/ThreadPlanRunToAddress.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanRunToAddress.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" @@ -73,6 +69,8 @@ void ThreadPlanRunToAddress::SetInitialBreakpoints() { ->CreateBreakpoint(m_addresses[i], true, false) .get(); if (breakpoint != nullptr) { + if (breakpoint->IsHardware() && !breakpoint->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; m_break_ids[i] = breakpoint->GetID(); breakpoint->SetThreadID(m_thread.GetID()); breakpoint->SetBreakpointKind("run-to-address"); @@ -85,6 +83,7 @@ ThreadPlanRunToAddress::~ThreadPlanRunToAddress() { for (size_t i = 0; i < num_break_ids; i++) { m_thread.CalculateTarget()->RemoveBreakpointByID(m_break_ids[i]); } + m_could_not_resolve_hw_bp = false; } void ThreadPlanRunToAddress::GetDescription(Stream *s, @@ -133,10 +132,15 @@ void ThreadPlanRunToAddress::GetDescription(Stream *s, } bool ThreadPlanRunToAddress::ValidatePlan(Stream *error) { + if (m_could_not_resolve_hw_bp) { + if (error) + error->Printf("Could not set hardware breakpoint(s)"); + return false; + } + // If we couldn't set the breakpoint for some reason, then this won't work. bool all_bps_good = true; size_t num_break_ids = m_break_ids.size(); - for (size_t i = 0; i < num_break_ids; i++) { if (m_break_ids[i] == LLDB_INVALID_BREAK_ID) { all_bps_good = false; diff --git a/source/Target/ThreadPlanShouldStopHere.cpp b/source/Target/ThreadPlanShouldStopHere.cpp index c7afe0d9a8a7..8062d8059c14 100644 --- a/source/Target/ThreadPlanShouldStopHere.cpp +++ b/source/Target/ThreadPlanShouldStopHere.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanShouldStopHere.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/RegisterContext.h" @@ -43,11 +39,11 @@ ThreadPlanShouldStopHere::ThreadPlanShouldStopHere( ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere() = default; bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback( - FrameComparison operation) { + FrameComparison operation, Status &status) { bool should_stop_here = true; if (m_callbacks.should_stop_here_callback) { should_stop_here = m_callbacks.should_stop_here_callback( - m_owner, m_flags, operation, m_baton); + m_owner, m_flags, operation, status, m_baton); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) { lldb::addr_t current_addr = @@ -63,7 +59,7 @@ bool ThreadPlanShouldStopHere::InvokeShouldStopHereCallback( bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( ThreadPlan *current_plan, Flags &flags, FrameComparison operation, - void *baton) { + Status &status, void *baton) { bool should_stop_here = true; StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); if (!frame) @@ -100,7 +96,7 @@ bool ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( ThreadPlan *current_plan, Flags &flags, FrameComparison operation, - void *baton) { + Status &status, void *baton) { const bool stop_others = false; const size_t frame_index = 0; ThreadPlanSP return_plan_sp; @@ -137,8 +133,8 @@ ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( "Queueing StepInRange plan to step through line 0 code."); return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepInRange( - false, range, sc, NULL, eOnlyDuringStepping, eLazyBoolCalculate, - eLazyBoolNo); + false, range, sc, NULL, eOnlyDuringStepping, status, + eLazyBoolCalculate, eLazyBoolNo); } } @@ -146,24 +142,25 @@ ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop( false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, - frame_index, true); + frame_index, status, true); return return_plan_sp; } ThreadPlanSP ThreadPlanShouldStopHere::QueueStepOutFromHerePlan( - lldb_private::Flags &flags, lldb::FrameComparison operation) { + lldb_private::Flags &flags, lldb::FrameComparison operation, + Status &status) { ThreadPlanSP return_plan_sp; if (m_callbacks.step_from_here_callback) { - return_plan_sp = - m_callbacks.step_from_here_callback(m_owner, flags, operation, m_baton); + return_plan_sp = m_callbacks.step_from_here_callback( + m_owner, flags, operation, status, m_baton); } return return_plan_sp; } lldb::ThreadPlanSP ThreadPlanShouldStopHere::CheckShouldStopHereAndQueueStepOut( - lldb::FrameComparison operation) { - if (!InvokeShouldStopHereCallback(operation)) - return QueueStepOutFromHerePlan(m_flags, operation); + lldb::FrameComparison operation, Status &status) { + if (!InvokeShouldStopHereCallback(operation, status)) + return QueueStepOutFromHerePlan(m_flags, operation, status); else return ThreadPlanSP(); } diff --git a/source/Target/ThreadPlanStepInRange.cpp b/source/Target/ThreadPlanStepInRange.cpp index 6405c135a33e..8f9889a9d68c 100644 --- a/source/Target/ThreadPlanStepInRange.cpp +++ b/source/Target/ThreadPlanStepInRange.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Core/Architecture.h" #include "lldb/Core/Module.h" @@ -112,8 +108,16 @@ void ThreadPlanStepInRange::SetupAvoidNoDebug( void ThreadPlanStepInRange::GetDescription(Stream *s, lldb::DescriptionLevel level) { + + auto PrintFailureIfAny = [&]() { + if (m_status.Success()) + return; + s->Printf(" failed (%s)", m_status.AsCString()); + }; + if (level == lldb::eDescriptionLevelBrief) { s->Printf("step in"); + PrintFailureIfAny(); return; } @@ -134,6 +138,8 @@ void ThreadPlanStepInRange::GetDescription(Stream *s, DumpRanges(s); } + PrintFailureIfAny(); + s->PutChar('.'); } @@ -166,7 +172,8 @@ bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { // ShouldStopHere plan, and otherwise we're done. // FIXME - This can be both a step in and a step out. Probably should // record which in the m_virtual_step. - m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(eFrameCompareYounger); + m_sub_plan_sp = + CheckShouldStopHereAndQueueStepOut(eFrameCompareYounger, m_status); } else { // Stepping through should be done running other threads in general, since // we're setting a breakpoint and continuing. So only stop others if we @@ -185,11 +192,12 @@ bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { // I'm going to make the assumption that you wouldn't RETURN to a // trampoline. So if we are in a trampoline we think the frame is older // because the trampoline confused the backtracer. - m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, - stop_others); + m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); if (!m_sub_plan_sp) { // Otherwise check the ShouldStopHere for step out: - m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order); + m_sub_plan_sp = + CheckShouldStopHereAndQueueStepOut(frame_order, m_status); if (log) { if (m_sub_plan_sp) log->Printf("ShouldStopHere found plan to step out of this frame."); @@ -227,8 +235,8 @@ bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { // We may have set the plan up above in the FrameIsOlder section: if (!m_sub_plan_sp) - m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, - stop_others); + m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); if (log) { if (m_sub_plan_sp) @@ -240,7 +248,7 @@ bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { // If not, give the "should_stop" callback a chance to push a plan to get // us out of here. But only do that if we actually have stepped in. if (!m_sub_plan_sp && frame_order == eFrameCompareYounger) - m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order); + m_sub_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order, m_status); // If we've stepped in and we are going to stop here, check to see if we // were asked to run past the prologue, and if so do that. @@ -288,7 +296,7 @@ bool ThreadPlanStepInRange::ShouldStop(Event *event_ptr) { log->Printf("Pushing past prologue "); m_sub_plan_sp = m_thread.QueueThreadPlanForRunToAddress( - false, func_start_address, true); + false, func_start_address, true, m_status); } } } @@ -384,7 +392,7 @@ bool ThreadPlanStepInRange::FrameMatchesAvoidCriteria() { bool ThreadPlanStepInRange::DefaultShouldStopHereCallback( ThreadPlan *current_plan, Flags &flags, FrameComparison operation, - void *baton) { + Status &status, void *baton) { bool should_stop_here = true; StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); @@ -392,7 +400,7 @@ bool ThreadPlanStepInRange::DefaultShouldStopHereCallback( // First see if the ThreadPlanShouldStopHere default implementation thinks we // should get out of here: should_stop_here = ThreadPlanShouldStopHere::DefaultShouldStopHereCallback( - current_plan, flags, operation, baton); + current_plan, flags, operation, status, baton); if (!should_stop_here) return should_stop_here; diff --git a/source/Target/ThreadPlanStepInstruction.cpp b/source/Target/ThreadPlanStepInstruction.cpp index dd8f129a935b..7707454c9798 100644 --- a/source/Target/ThreadPlanStepInstruction.cpp +++ b/source/Target/ThreadPlanStepInstruction.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" @@ -57,11 +53,19 @@ void ThreadPlanStepInstruction::SetUpState() { void ThreadPlanStepInstruction::GetDescription(Stream *s, lldb::DescriptionLevel level) { + auto PrintFailureIfAny = [&]() { + if (m_status.Success()) + return; + s->Printf(" failed (%s)", m_status.AsCString()); + }; + if (level == lldb::eDescriptionLevelBrief) { if (m_step_over) s->Printf("instruction step over"); else s->Printf("instruction step into"); + + PrintFailureIfAny(); } else { s->Printf("Stepping one instruction past "); s->Address(m_instruction_addr, sizeof(addr_t)); @@ -72,6 +76,8 @@ void ThreadPlanStepInstruction::GetDescription(Stream *s, s->Printf(" stepping over calls"); else s->Printf(" stepping into calls"); + + PrintFailureIfAny(); } } @@ -192,7 +198,8 @@ bool ThreadPlanStepInstruction::ShouldStop(Event *event_ptr) { // for now it is safer to run others. const bool stop_others = false; m_thread.QueueThreadPlanForStepOutNoShouldStop( - false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0); + false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0, + m_status); return false; } else { if (log) { diff --git a/source/Target/ThreadPlanStepOut.cpp b/source/Target/ThreadPlanStepOut.cpp index f2db6e4b6f42..378de53fafd7 100644 --- a/source/Target/ThreadPlanStepOut.cpp +++ b/source/Target/ThreadPlanStepOut.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Core/Value.h" @@ -48,18 +44,36 @@ ThreadPlanStepOut::ThreadPlanStepOut( m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others), m_immediate_step_from_function(nullptr), m_calculate_return_value(gather_return_value) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); SetFlagsToDefault(); SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0); - StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1)); + uint32_t return_frame_index = frame_idx + 1; + StackFrameSP return_frame_sp( + m_thread.GetStackFrameAtIndex(return_frame_index)); StackFrameSP immediate_return_from_sp( m_thread.GetStackFrameAtIndex(frame_idx)); if (!return_frame_sp || !immediate_return_from_sp) return; // we can't do anything here. ValidatePlan() will return false. + // While stepping out, behave as-if artificial frames are not present. + while (return_frame_sp->IsArtificial()) { + m_stepped_past_frames.push_back(return_frame_sp); + + ++return_frame_index; + return_frame_sp = m_thread.GetStackFrameAtIndex(return_frame_index); + + // We never expect to see an artificial frame without a regular ancestor. + // If this happens, log the issue and defensively refuse to step out. + if (!return_frame_sp) { + LLDB_LOG(log, "Can't step out of frame with artificial ancestors"); + return; + } + } + m_step_out_to_id = return_frame_sp->GetStackID(); m_immediate_step_from_id = immediate_return_from_sp->GetStackID(); @@ -67,7 +81,7 @@ ThreadPlanStepOut::ThreadPlanStepOut( // have to be a little more careful. It is non-trivial to determine the real // "return code address" for an inlined frame, so we have to work our way to // that frame and then step out. - if (immediate_return_from_sp && immediate_return_from_sp->IsInlined()) { + if (immediate_return_from_sp->IsInlined()) { if (frame_idx > 0) { // First queue a plan that gets us to this inlined frame, and when we get // there we'll queue a second plan that walks us out of this frame. @@ -82,7 +96,7 @@ ThreadPlanStepOut::ThreadPlanStepOut( // just do that now. QueueInlinedStepPlan(false); } - } else if (return_frame_sp) { + } else { // Find the return address and set a breakpoint there: // FIXME - can we do this more securely if we know first_insn? @@ -115,7 +129,10 @@ ThreadPlanStepOut::ThreadPlanStepOut( Breakpoint *return_bp = m_thread.CalculateTarget() ->CreateBreakpoint(m_return_addr, true, false) .get(); + if (return_bp != nullptr) { + if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; return_bp->SetThreadID(m_thread.GetID()); m_return_bp_id = return_bp->GetID(); return_bp->SetBreakpointKind("step-out"); @@ -198,19 +215,35 @@ void ThreadPlanStepOut::GetDescription(Stream *s, s->Printf(" using breakpoint site %d", m_return_bp_id); } } + + s->Printf("\n"); + for (StackFrameSP frame_sp : m_stepped_past_frames) { + s->Printf("Stepped out past: "); + frame_sp->DumpUsingSettingsFormat(s); + } } bool ThreadPlanStepOut::ValidatePlan(Stream *error) { if (m_step_out_to_inline_plan_sp) return m_step_out_to_inline_plan_sp->ValidatePlan(error); - else if (m_step_through_inline_plan_sp) + + if (m_step_through_inline_plan_sp) return m_step_through_inline_plan_sp->ValidatePlan(error); - else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) { + + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } + + if (m_return_bp_id == LLDB_INVALID_BREAK_ID) { if (error) error->PutCString("Could not create return address breakpoint."); return false; - } else - return true; + } + + return true; } bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) { @@ -257,7 +290,7 @@ bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) { } if (done) { - if (InvokeShouldStopHereCallback(eFrameCompareOlder)) { + if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) { CalculateReturnValue(); SetPlanComplete(); } @@ -319,12 +352,12 @@ bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) { // is consult the ShouldStopHere, and we are done. if (done) { - if (InvokeShouldStopHereCallback(eFrameCompareOlder)) { + if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) { CalculateReturnValue(); SetPlanComplete(); } else { m_step_out_further_plan_sp = - QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder); + QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder, m_status); done = false; } } diff --git a/source/Target/ThreadPlanStepOverBreakpoint.cpp b/source/Target/ThreadPlanStepOverBreakpoint.cpp index 7497dbff1729..8b24bf94973b 100644 --- a/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -9,10 +9,6 @@ #include "lldb/Target/ThreadPlanStepOverBreakpoint.h" -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Utility/Log.h" diff --git a/source/Target/ThreadPlanStepOverRange.cpp b/source/Target/ThreadPlanStepOverRange.cpp index b88d25b707eb..129f30371f8d 100644 --- a/source/Target/ThreadPlanStepOverRange.cpp +++ b/source/Target/ThreadPlanStepOverRange.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepOverRange.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/CompileUnit.h" @@ -51,10 +47,18 @@ ThreadPlanStepOverRange::~ThreadPlanStepOverRange() = default; void ThreadPlanStepOverRange::GetDescription(Stream *s, lldb::DescriptionLevel level) { + auto PrintFailureIfAny = [&]() { + if (m_status.Success()) + return; + s->Printf(" failed (%s)", m_status.AsCString()); + }; + if (level == lldb::eDescriptionLevelBrief) { s->Printf("step over"); + PrintFailureIfAny(); return; } + s->Printf("Stepping over"); bool printed_line_info = false; if (m_addr_context.line_entry.IsValid()) { @@ -68,6 +72,8 @@ void ThreadPlanStepOverRange::GetDescription(Stream *s, DumpRanges(s); } + PrintFailureIfAny(); + s->PutChar('.'); } @@ -117,10 +123,7 @@ bool ThreadPlanStepOverRange::IsEquivalentContext( } } // Fall back to symbol if we have no decision from comp_unit/function/block. - if (m_addr_context.symbol && m_addr_context.symbol == context.symbol) { - return true; - } - return false; + return m_addr_context.symbol && m_addr_context.symbol == context.symbol; } bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { @@ -151,8 +154,8 @@ bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { // because the trampoline confused the backtracer. As below, we step // through first, and then try to figure out how to get back out again. - new_plan_sp = - m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, stop_others); + new_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, + stop_others, m_status); if (new_plan_sp && log) log->Printf( @@ -173,11 +176,11 @@ bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { if (IsEquivalentContext(older_context)) { new_plan_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop( false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, 0, - true); + m_status, true); break; } else { - new_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, - stop_others); + new_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); // If we found a way through, then we should stop recursing. if (new_plan_sp) break; @@ -196,8 +199,8 @@ bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { // we are in a stub then it's likely going to be hard to get out from // here. It is probably easiest to step into the stub, and then it will // be straight-forward to step out. - new_plan_sp = m_thread.QueueThreadPlanForStepThrough(m_stack_id, false, - stop_others); + new_plan_sp = m_thread.QueueThreadPlanForStepThrough( + m_stack_id, false, stop_others, m_status); } else { // The current clang (at least through 424) doesn't always get the // address range for the DW_TAG_inlined_subroutines right, so that when @@ -287,8 +290,8 @@ bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { cur_pc); new_plan_sp = m_thread.QueueThreadPlanForStepOverRange( - abort_other_plans, step_range, sc, - stop_other_threads); + abort_other_plans, step_range, sc, stop_other_threads, + m_status); break; } look_ahead_step++; @@ -309,7 +312,7 @@ bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { // If we haven't figured out something to do yet, then ask the ShouldStopHere // callback: if (!new_plan_sp) { - new_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order); + new_plan_sp = CheckShouldStopHereAndQueueStepOut(frame_order, m_status); } if (!new_plan_sp) @@ -323,7 +326,7 @@ bool ThreadPlanStepOverRange::ShouldStop(Event *event_ptr) { if (!new_plan_sp) { // For efficiencies sake, we know we're done here so we don't have to do // this calculation again in MischiefManaged. - SetPlanComplete(); + SetPlanComplete(m_status.Success()); return true; } else return false; diff --git a/source/Target/ThreadPlanStepRange.cpp b/source/Target/ThreadPlanStepRange.cpp index 5a71119015eb..7ba68ee84981 100644 --- a/source/Target/ThreadPlanStepRange.cpp +++ b/source/Target/ThreadPlanStepRange.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepRange.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/BreakpointSite.h" @@ -61,7 +57,15 @@ void ThreadPlanStepRange::DidPush() { SetNextBranchBreakpoint(); } -bool ThreadPlanStepRange::ValidatePlan(Stream *error) { return true; } +bool ThreadPlanStepRange::ValidatePlan(Stream *error) { + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } + return true; +} Vote ThreadPlanStepRange::ShouldReportStop(Event *event_ptr) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); @@ -285,6 +289,7 @@ void ThreadPlanStepRange::ClearNextBranchBreakpoint() { m_next_branch_bp_sp->GetID()); GetTarget().RemoveBreakpointByID(m_next_branch_bp_sp->GetID()); m_next_branch_bp_sp.reset(); + m_could_not_resolve_hw_bp = false; } } @@ -335,6 +340,11 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { m_next_branch_bp_sp = GetTarget().CreateBreakpoint(run_to_address, is_internal, false); if (m_next_branch_bp_sp) { + + if (m_next_branch_bp_sp->IsHardware() && + !m_next_branch_bp_sp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; + if (log) { lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; BreakpointLocationSP bp_loc = @@ -351,8 +361,10 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { run_to_address.GetLoadAddress( &m_thread.GetProcess()->GetTarget())); } + m_next_branch_bp_sp->SetThreadID(m_thread.GetID()); m_next_branch_bp_sp->SetBreakpointKind("next-branch-location"); + return true; } else return false; diff --git a/source/Target/ThreadPlanStepThrough.cpp b/source/Target/ThreadPlanStepThrough.cpp index c039a32f5515..d1f3c2219f6d 100644 --- a/source/Target/ThreadPlanStepThrough.cpp +++ b/source/Target/ThreadPlanStepThrough.cpp @@ -7,12 +7,9 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepThrough.h" #include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" @@ -61,7 +58,10 @@ ThreadPlanStepThrough::ThreadPlanStepThrough(Thread &thread, ->GetTarget() .CreateBreakpoint(m_backstop_addr, true, false) .get(); + if (return_bp != nullptr) { + if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; return_bp->SetThreadID(m_thread.GetID()); m_backstop_bkpt_id = return_bp->GetID(); return_bp->SetBreakpointKind("step-through-backstop"); @@ -95,6 +95,15 @@ void ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC() { if (objc_runtime) m_sub_plan_sp = objc_runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others); + + CPPLanguageRuntime *cpp_runtime = + m_thread.GetProcess()->GetCPPLanguageRuntime(); + + // If the ObjC runtime did not provide us with a step though plan then if we + // have it check the C++ runtime for a step though plan. + if (!m_sub_plan_sp.get() && cpp_runtime) + m_sub_plan_sp = + cpp_runtime->GetStepThroughTrampolinePlan(m_thread, m_stop_others); } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); @@ -129,7 +138,26 @@ void ThreadPlanStepThrough::GetDescription(Stream *s, } bool ThreadPlanStepThrough::ValidatePlan(Stream *error) { - return m_sub_plan_sp.get() != nullptr; + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } + + if (m_backstop_bkpt_id == LLDB_INVALID_BREAK_ID) { + if (error) + error->PutCString("Could not create backstop breakpoint."); + return false; + } + + if (!m_sub_plan_sp.get()) { + if (error) + error->PutCString("Does not have a subplan."); + return false; + } + + return true; } bool ThreadPlanStepThrough::DoPlanExplainsStop(Event *event_ptr) { @@ -205,6 +233,7 @@ void ThreadPlanStepThrough::ClearBackstopBreakpoint() { if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID) { m_thread.GetProcess()->GetTarget().RemoveBreakpointByID(m_backstop_bkpt_id); m_backstop_bkpt_id = LLDB_INVALID_BREAK_ID; + m_could_not_resolve_hw_bp = false; } } diff --git a/source/Target/ThreadPlanStepUntil.cpp b/source/Target/ThreadPlanStepUntil.cpp index 9984ee925c86..1335c62ba94c 100644 --- a/source/Target/ThreadPlanStepUntil.cpp +++ b/source/Target/ThreadPlanStepUntil.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadPlanStepUntil.h" #include "lldb/Breakpoint/Breakpoint.h" @@ -57,7 +53,10 @@ ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread, m_return_addr = return_frame_sp->GetStackID().GetPC(); Breakpoint *return_bp = target_sp->CreateBreakpoint(m_return_addr, true, false).get(); + if (return_bp != nullptr) { + if (return_bp->IsHardware() && !return_bp->HasResolvedLocations()) + m_could_not_resolve_hw_bp = true; return_bp->SetThreadID(thread_id); m_return_bp_id = return_bp->GetID(); return_bp->SetBreakpointKind("until-return-backstop"); @@ -97,6 +96,7 @@ void ThreadPlanStepUntil::Clear() { } } m_until_points.clear(); + m_could_not_resolve_hw_bp = false; } void ThreadPlanStepUntil::GetDescription(Stream *s, @@ -127,9 +127,16 @@ void ThreadPlanStepUntil::GetDescription(Stream *s, } bool ThreadPlanStepUntil::ValidatePlan(Stream *error) { - if (m_return_bp_id == LLDB_INVALID_BREAK_ID) + if (m_could_not_resolve_hw_bp) { + if (error) + error->PutCString( + "Could not create hardware breakpoint for thread plan."); + return false; + } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) { + if (error) + error->PutCString("Could not create return breakpoint."); return false; - else { + } else { until_collection::iterator pos, end = m_until_points.end(); for (pos = m_until_points.begin(); pos != end; pos++) { if (!LLDB_BREAK_ID_IS_VALID((*pos).second)) diff --git a/source/Target/ThreadPlanTracer.cpp b/source/Target/ThreadPlanTracer.cpp index acbbd4d8bcc2..04e29fd26080 100644 --- a/source/Target/ThreadPlanTracer.cpp +++ b/source/Target/ThreadPlanTracer.cpp @@ -7,15 +7,12 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes #include <cstring> #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/DumpRegisterValue.h" #include "lldb/Core/Module.h" -#include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Value.h" #include "lldb/Symbol/TypeList.h" @@ -30,6 +27,7 @@ #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Target/ThreadSpec.cpp b/source/Target/ThreadSpec.cpp index 444a5a5b262a..bae3d0b2238f 100644 --- a/source/Target/ThreadSpec.cpp +++ b/source/Target/ThreadSpec.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/ThreadSpec.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/StructuredData.h" diff --git a/source/Target/UnixSignals.cpp b/source/Target/UnixSignals.cpp index 9cfb1b74adee..1448535b8be4 100644 --- a/source/Target/UnixSignals.cpp +++ b/source/Target/UnixSignals.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/UnixSignals.h" #include "Plugins/Process/Utility/FreeBSDSignals.h" #include "Plugins/Process/Utility/LinuxSignals.h" @@ -88,7 +84,7 @@ void UnixSignals::Reset() { AddSignal(10, "SIGBUS", false, true, true, "bus error"); AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation"); AddSignal(12, "SIGSYS", false, true, true, "bad argument to system call"); - AddSignal(13, "SIGPIPE", false, true, true, + AddSignal(13, "SIGPIPE", false, false, false, "write on a pipe with no one to read it"); AddSignal(14, "SIGALRM", false, false, false, "alarm clock"); AddSignal(15, "SIGTERM", false, true, true, diff --git a/source/Target/UnwindAssembly.cpp b/source/Target/UnwindAssembly.cpp index 3b2b7bf17025..06b6aef28da7 100644 --- a/source/Target/UnwindAssembly.cpp +++ b/source/Target/UnwindAssembly.cpp @@ -7,10 +7,6 @@ // //===----------------------------------------------------------------------===// -// C Includes -// C++ Includes -// Other libraries and framework includes -// Project includes #include "lldb/Target/UnwindAssembly.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Core/PluginManager.h" |