diff options
Diffstat (limited to 'lldb/source/Target')
28 files changed, 1369 insertions, 446 deletions
diff --git a/lldb/source/Target/JITLoaderList.cpp b/lldb/source/Target/JITLoaderList.cpp index 92e841b5c94d..9158d0a5e546 100644 --- a/lldb/source/Target/JITLoaderList.cpp +++ b/lldb/source/Target/JITLoaderList.cpp @@ -24,9 +24,7 @@ void JITLoaderList::Append(const JITLoaderSP &jit_loader_sp) { void JITLoaderList::Remove(const JITLoaderSP &jit_loader_sp) { std::lock_guard<std::recursive_mutex> guard(m_jit_loaders_mutex); - m_jit_loaders_vec.erase(std::remove(m_jit_loaders_vec.begin(), - m_jit_loaders_vec.end(), jit_loader_sp), - m_jit_loaders_vec.end()); + llvm::erase_value(m_jit_loaders_vec, jit_loader_sp); } size_t JITLoaderList::GetSize() const { return m_jit_loaders_vec.size(); } diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index 6df36aeeb7b7..892d2a86437e 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -144,7 +144,7 @@ Language::GetHardcodedSynthetics() { return {}; } -std::vector<ConstString> +std::vector<FormattersMatchCandidate> Language::GetPossibleFormattersMatches(ValueObject &valobj, lldb::DynamicValueType use_dynamic) { return {}; @@ -221,6 +221,17 @@ const char *Language::GetNameForLanguageType(LanguageType language) { return language_names[eLanguageTypeUnknown].name; } +void Language::PrintSupportedLanguagesForExpressions(Stream &s, + llvm::StringRef prefix, + llvm::StringRef suffix) { + auto supported = Language::GetLanguagesSupportingTypeSystemsForExpressions(); + for (size_t idx = 0; idx < num_languages; ++idx) { + auto const &lang = language_names[idx]; + if (supported[lang.type]) + s << prefix << lang.name << suffix; + } +} + void Language::PrintAllLanguages(Stream &s, const char *prefix, const char *suffix) { for (uint32_t i = 1; i < num_languages; i++) { diff --git a/lldb/source/Target/Memory.cpp b/lldb/source/Target/Memory.cpp index a07a72f31922..d4dedeea7c2c 100644 --- a/lldb/source/Target/Memory.cpp +++ b/lldb/source/Target/Memory.cpp @@ -332,9 +332,9 @@ AllocatedMemoryCache::AllocatedMemoryCache(Process &process) AllocatedMemoryCache::~AllocatedMemoryCache() = default; -void AllocatedMemoryCache::Clear() { +void AllocatedMemoryCache::Clear(bool deallocate_memory) { std::lock_guard<std::recursive_mutex> guard(m_mutex); - if (m_process.IsAlive()) { + if (m_process.IsAlive() && deallocate_memory) { PermissionsToBlockMap::iterator pos, end = m_memory_map.end(); for (pos = m_memory_map.begin(); pos != end; ++pos) m_process.DoDeallocateMemory(pos->second->GetBaseAddress()); diff --git a/lldb/source/Target/MemoryTagMap.cpp b/lldb/source/Target/MemoryTagMap.cpp index 846eef9209da..c5a7c85ac09e 100644 --- a/lldb/source/Target/MemoryTagMap.cpp +++ b/lldb/source/Target/MemoryTagMap.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Target/MemoryTagMap.h" +#include <optional> using namespace lldb_private; @@ -27,38 +28,38 @@ void MemoryTagMap::InsertTags(lldb::addr_t addr, bool MemoryTagMap::Empty() const { return m_addr_to_tag.empty(); } -std::vector<llvm::Optional<lldb::addr_t>> +std::vector<std::optional<lldb::addr_t>> MemoryTagMap::GetTags(lldb::addr_t addr, size_t len) const { // Addr and len might be unaligned addr = m_manager->RemoveTagBits(addr); MemoryTagManager::TagRange range(addr, len); range = m_manager->ExpandToGranule(range); - std::vector<llvm::Optional<lldb::addr_t>> tags; + std::vector<std::optional<lldb::addr_t>> tags; lldb::addr_t end_addr = range.GetRangeEnd(); addr = range.GetRangeBase(); bool got_valid_tags = false; size_t granule_size = m_manager->GetGranuleSize(); for (; addr < end_addr; addr += granule_size) { - llvm::Optional<lldb::addr_t> tag = GetTag(addr); + std::optional<lldb::addr_t> tag = GetTag(addr); tags.push_back(tag); if (tag) got_valid_tags = true; } - // To save the caller checking if every item is llvm::None, + // To save the caller checking if every item is std::nullopt, // we return an empty vector if we got no tags at all. if (got_valid_tags) return tags; return {}; } -llvm::Optional<lldb::addr_t> MemoryTagMap::GetTag(lldb::addr_t addr) const { +std::optional<lldb::addr_t> MemoryTagMap::GetTag(lldb::addr_t addr) const { // Here we assume that addr is granule aligned, just like when the tags // were inserted. auto found = m_addr_to_tag.find(addr); if (found == m_addr_to_tag.end()) - return llvm::None; + return std::nullopt; return found->second; } diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp index 4ebb175fcd85..13bb50b362c6 100644 --- a/lldb/source/Target/PathMappingList.cpp +++ b/lldb/source/Target/PathMappingList.cpp @@ -8,6 +8,7 @@ #include <climits> #include <cstring> +#include <optional> #include "lldb/Host/FileSystem.h" #include "lldb/Host/PosixApi.h" @@ -76,6 +77,19 @@ void PathMappingList::Append(const PathMappingList &rhs, bool notify) { } } +bool PathMappingList::AppendUnique(llvm::StringRef path, + llvm::StringRef replacement, bool notify) { + auto normalized_path = NormalizePath(path); + auto normalized_replacement = NormalizePath(replacement); + for (const auto &pair : m_pairs) { + if (pair.first.GetStringRef().equals(normalized_path) && + pair.second.GetStringRef().equals(normalized_replacement)) + return false; + } + Append(path, replacement, notify); + return true; +} + void PathMappingList::Insert(llvm::StringRef path, llvm::StringRef replacement, uint32_t index, bool notify) { ++m_mod_id; @@ -131,6 +145,16 @@ void PathMappingList::Dump(Stream *s, int pair_index) { } } +llvm::json::Value PathMappingList::ToJSON() { + llvm::json::Array entries; + for (const auto &pair : m_pairs) { + llvm::json::Array entry{pair.first.GetStringRef().str(), + pair.second.GetStringRef().str()}; + entries.emplace_back(std::move(entry)); + } + return entries; +} + void PathMappingList::Clear(bool notify) { if (!m_pairs.empty()) ++m_mod_id; @@ -141,7 +165,7 @@ void PathMappingList::Clear(bool notify) { bool PathMappingList::RemapPath(ConstString path, ConstString &new_path) const { - if (llvm::Optional<FileSpec> remapped = RemapPath(path.GetStringRef())) { + if (std::optional<FileSpec> remapped = RemapPath(path.GetStringRef())) { new_path.SetString(remapped->GetPath()); return true; } @@ -151,18 +175,17 @@ bool PathMappingList::RemapPath(ConstString path, /// Append components to path, applying style. static void AppendPathComponents(FileSpec &path, llvm::StringRef components, llvm::sys::path::Style style) { - auto component = llvm::sys::path::begin(components, style); - auto e = llvm::sys::path::end(components); - while (component != e && - llvm::sys::path::is_separator(*component->data(), style)) - ++component; - for (; component != e; ++component) - path.AppendPathComponent(*component); + auto component = llvm::sys::path::begin(components, style); + auto e = llvm::sys::path::end(components); + while (component != e && + llvm::sys::path::is_separator(*component->data(), style)) + ++component; + for (; component != e; ++component) + path.AppendPathComponent(*component); } -llvm::Optional<FileSpec> -PathMappingList::RemapPath(llvm::StringRef mapping_path, - bool only_if_exists) const { +std::optional<FileSpec> PathMappingList::RemapPath(llvm::StringRef mapping_path, + bool only_if_exists) const { if (m_pairs.empty() || mapping_path.empty()) return {}; LazyBool path_is_relative = eLazyBoolCalculate; @@ -197,10 +220,12 @@ PathMappingList::RemapPath(llvm::StringRef mapping_path, return {}; } -bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const { +std::optional<llvm::StringRef> +PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) const { std::string path = file.GetPath(); llvm::StringRef path_ref(path); for (const auto &it : m_pairs) { + llvm::StringRef removed_prefix = it.second.GetStringRef(); if (!path_ref.consume_front(it.second.GetStringRef())) continue; auto orig_file = it.first.GetStringRef(); @@ -208,12 +233,13 @@ bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) co llvm::sys::path::Style::native); fixed.SetFile(orig_file, orig_style); AppendPathComponents(fixed, path_ref, orig_style); - return true; + return removed_prefix; } - return false; + return std::nullopt; } -llvm::Optional<FileSpec> PathMappingList::FindFile(const FileSpec &orig_spec) const { +std::optional<FileSpec> +PathMappingList::FindFile(const FileSpec &orig_spec) const { // We must normalize the orig_spec again using the host's path style, // otherwise there will be mismatch between the host and remote platform // if they use different path styles. diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 559f7664c72e..1ddd7596280e 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -10,6 +10,7 @@ #include <csignal> #include <fstream> #include <memory> +#include <optional> #include <vector> #include "lldb/Breakpoint/BreakpointIDList.h" @@ -298,7 +299,7 @@ void Platform::GetStatus(Stream &strm) { if (!os_version.empty()) { strm.Format("OS Version: {0}", os_version.getAsString()); - if (llvm::Optional<std::string> s = GetOSBuildString()) + if (std::optional<std::string> s = GetOSBuildString()) strm.Format(" ({0})", *s); strm.EOL(); @@ -317,7 +318,7 @@ void Platform::GetStatus(Stream &strm) { strm.Format(" Sysroot: {0}\n", GetSDKRootDirectory()); } if (GetWorkingDirectory()) { - strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString()); + strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetPath().c_str()); } if (!IsConnected()) return; @@ -327,7 +328,7 @@ void Platform::GetStatus(Stream &strm) { if (!specific_info.empty()) strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); - if (llvm::Optional<std::string> s = GetOSKernelDescription()) + if (std::optional<std::string> s = GetOSKernelDescription()) strm.Format(" Kernel: {0}\n", *s); } @@ -373,13 +374,13 @@ llvm::VersionTuple Platform::GetOSVersion(Process *process) { return llvm::VersionTuple(); } -llvm::Optional<std::string> Platform::GetOSBuildString() { +std::optional<std::string> Platform::GetOSBuildString() { if (IsHost()) return HostInfo::GetOSBuildString(); return GetRemoteOSBuildString(); } -llvm::Optional<std::string> Platform::GetOSKernelDescription() { +std::optional<std::string> Platform::GetOSKernelDescription() { if (IsHost()) return HostInfo::GetOSKernelDescription(); return GetRemoteOSKernelDescription(); @@ -434,12 +435,13 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, // make the new directory and get in there FileSpec dst_dir = rc_baton->dst; if (!dst_dir.GetFilename()) - dst_dir.GetFilename() = src.GetLastPathComponent(); + dst_dir.SetFilename(src.GetLastPathComponent()); Status error = rc_baton->platform_ptr->MakeDirectory( dst_dir, lldb::eFilePermissionsDirectoryDefault); if (error.Fail()) { rc_baton->error.SetErrorStringWithFormat( - "unable to setup directory %s on remote end", dst_dir.GetCString()); + "unable to setup directory %s on remote end", + dst_dir.GetPath().c_str()); return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out } @@ -449,7 +451,7 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, // Make a filespec that only fills in the directory of a FileSpec so when // we enumerate we can quickly fill in the filename for dst copies FileSpec recurse_dst; - recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str()); + recurse_dst.SetDirectory(dst_dir.GetPathAsConstString()); RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr, Status()}; FileSystem::Instance().EnumerateDirectory(src_dir_path, true, true, true, @@ -465,7 +467,7 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, // copy the file and keep going FileSpec dst_file = rc_baton->dst; if (!dst_file.GetFilename()) - dst_file.GetFilename() = src.GetFilename(); + dst_file.SetFilename(src.GetFilename()); FileSpec src_resolved; @@ -487,7 +489,7 @@ RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft, // copy the file and keep going FileSpec dst_file = rc_baton->dst; if (!dst_file.GetFilename()) - dst_file.GetFilename() = src.GetFilename(); + dst_file.SetFilename(src.GetFilename()); Status err = rc_baton->platform_ptr->PutFile(src, dst_file); if (err.Fail()) { rc_baton->error.SetErrorString(err.AsCString()); @@ -514,7 +516,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { FileSpec fixed_dst(dst); if (!fixed_dst.GetFilename()) - fixed_dst.GetFilename() = src.GetFilename(); + fixed_dst.SetFilename(src.GetFilename()); FileSpec working_dir = GetWorkingDirectory(); @@ -522,7 +524,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { if (dst.GetDirectory()) { const char first_dst_dir_char = dst.GetDirectory().GetCString()[0]; if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') { - fixed_dst.GetDirectory() = dst.GetDirectory(); + fixed_dst.SetDirectory(dst.GetDirectory()); } // If the fixed destination file doesn't have a directory yet, then we // must have a relative path. We will resolve this relative path against @@ -533,7 +535,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { if (working_dir) { relative_spec = working_dir; relative_spec.AppendPathComponent(dst.GetPath()); - fixed_dst.GetDirectory() = relative_spec.GetDirectory(); + fixed_dst.SetDirectory(relative_spec.GetDirectory()); } else { error.SetErrorStringWithFormat( "platform working directory must be valid for relative path '%s'", @@ -543,7 +545,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { } } else { if (working_dir) { - fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); + fixed_dst.SetDirectory(working_dir.GetPathAsConstString()); } else { error.SetErrorStringWithFormat( "platform working directory must be valid for relative path '%s'", @@ -553,7 +555,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { } } else { if (working_dir) { - fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); + fixed_dst.SetDirectory(working_dir.GetPathAsConstString()); } else { error.SetErrorStringWithFormat("platform working directory must be valid " "when destination directory is empty"); @@ -580,7 +582,7 @@ Status Platform::Install(const FileSpec &src, const FileSpec &dst) { // Make a filespec that only fills in the directory of a FileSpec so // when we enumerate we can quickly fill in the filename for dst copies FileSpec recurse_dst; - recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString()); + recurse_dst.SetDirectory(fixed_dst.GetPathAsConstString()); std::string src_dir_path(src.GetPath()); RecurseCopyBaton baton = {recurse_dst, this, Status()}; FileSystem::Instance().EnumerateDirectory( @@ -737,7 +739,7 @@ ConstString Platform::GetFullNameForDylib(ConstString basename) { bool Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir) { Log *log = GetLog(LLDBLog::Platform); LLDB_LOGF(log, "Platform::SetRemoteWorkingDirectory('%s')", - working_dir.GetCString()); + working_dir.GetPath().c_str()); m_working_dir = working_dir; return true; } @@ -928,7 +930,8 @@ ArchSpec Platform::GetAugmentedArchSpec(llvm::StringRef triple) { ArchSpec compatible_arch; ArchSpec raw_arch(triple); - if (!IsCompatibleArchitecture(raw_arch, {}, false, &compatible_arch)) + if (!IsCompatibleArchitecture(raw_arch, {}, ArchSpec::CompatibleMatch, + &compatible_arch)) return raw_arch; if (!compatible_arch.IsValid()) @@ -1155,16 +1158,14 @@ Platform::CreateArchList(llvm::ArrayRef<llvm::Triple::ArchType> archs, /// architecture and the target triple contained within. bool Platform::IsCompatibleArchitecture(const ArchSpec &arch, const ArchSpec &process_host_arch, - bool exact_arch_match, + ArchSpec::MatchType match, ArchSpec *compatible_arch_ptr) { // If the architecture is invalid, we must answer true... if (arch.IsValid()) { ArchSpec platform_arch; - auto match = exact_arch_match ? &ArchSpec::IsExactMatch - : &ArchSpec::IsCompatibleMatch; for (const ArchSpec &platform_arch : GetSupportedArchitectures(process_host_arch)) { - if ((arch.*match)(platform_arch)) { + if (arch.IsMatch(platform_arch, match)) { if (compatible_arch_ptr) *compatible_arch_ptr = platform_arch; return true; @@ -1357,7 +1358,7 @@ static constexpr OptionDefinition g_caching_option_table[] = { }; llvm::ArrayRef<OptionDefinition> OptionGroupPlatformRSync::GetDefinitions() { - return llvm::makeArrayRef(g_rsync_option_table); + return llvm::ArrayRef(g_rsync_option_table); } void OptionGroupPlatformRSync::OptionParsingStarting( @@ -1405,7 +1406,7 @@ Platform::SetThreadCreationBreakpoint(lldb_private::Target &target) { } llvm::ArrayRef<OptionDefinition> OptionGroupPlatformSSH::GetDefinitions() { - return llvm::makeArrayRef(g_ssh_option_table); + return llvm::ArrayRef(g_ssh_option_table); } void OptionGroupPlatformSSH::OptionParsingStarting( @@ -1438,7 +1439,7 @@ OptionGroupPlatformSSH::SetOptionValue(uint32_t option_idx, } llvm::ArrayRef<OptionDefinition> OptionGroupPlatformCaching::GetDefinitions() { - return llvm::makeArrayRef(g_caching_option_table); + return llvm::ArrayRef(g_caching_option_table); } void OptionGroupPlatformCaching::OptionParsingStarting( @@ -1772,25 +1773,21 @@ lldb::ProcessSP Platform::DoConnectProcess(llvm::StringRef connect_url, error.Clear(); if (!target) { - ArchSpec arch; - if (target && target->GetArchitecture().IsValid()) - arch = target->GetArchitecture(); - else - arch = Target::GetDefaultArchitecture(); + ArchSpec arch = Target::GetDefaultArchitecture(); - const char *triple = ""; - if (arch.IsValid()) - triple = arch.GetTriple().getTriple().c_str(); + const char *triple = + arch.IsValid() ? arch.GetTriple().getTriple().c_str() : ""; TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget( debugger, "", triple, eLoadDependentsNo, nullptr, new_target_sp); + target = new_target_sp.get(); + if (!target || error.Fail()) { + return nullptr; + } } - if (!target || error.Fail()) - return nullptr; - lldb::ProcessSP process_sp = target->CreateProcess(debugger.GetListener(), plugin_name, nullptr, true); @@ -1814,7 +1811,7 @@ lldb::ProcessSP Platform::DoConnectProcess(llvm::StringRef connect_url, if (synchronous) { EventSP event_sp; - process_sp->WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp, + process_sp->WaitForProcessToStop(std::nullopt, &event_sp, true, listener_sp, nullptr); process_sp->RestoreProcessEvents(); bool pop_process_io_handler = false; @@ -1930,6 +1927,27 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, trap_opcode_size = sizeof(g_i386_opcode); } break; + case llvm::Triple::riscv32: + case llvm::Triple::riscv64: { + static const uint8_t g_riscv_opcode[] = {0x73, 0x00, 0x10, 0x00}; // ebreak + static const uint8_t g_riscv_opcode_c[] = {0x02, 0x90}; // c.ebreak + if (arch.GetFlags() & ArchSpec::eRISCV_rvc) { + trap_opcode = g_riscv_opcode_c; + trap_opcode_size = sizeof(g_riscv_opcode_c); + } else { + trap_opcode = g_riscv_opcode; + trap_opcode_size = sizeof(g_riscv_opcode); + } + } break; + + case llvm::Triple::loongarch32: + case llvm::Triple::loongarch64: { + static const uint8_t g_loongarch_opcode[] = {0x05, 0x00, 0x2a, + 0x00}; // break 0x5 + trap_opcode = g_loongarch_opcode; + trap_opcode_size = sizeof(g_loongarch_opcode); + } break; + default: return 0; } @@ -1965,14 +1983,15 @@ PlatformSP PlatformList::GetOrCreate(const ArchSpec &arch, std::lock_guard<std::recursive_mutex> guard(m_mutex); // First try exact arch matches across all platforms already created for (const auto &platform_sp : m_platforms) { - if (platform_sp->IsCompatibleArchitecture(arch, process_host_arch, true, - platform_arch_ptr)) + if (platform_sp->IsCompatibleArchitecture( + arch, process_host_arch, ArchSpec::ExactMatch, platform_arch_ptr)) return platform_sp; } // Next try compatible arch matches across all platforms already created for (const auto &platform_sp : m_platforms) { - if (platform_sp->IsCompatibleArchitecture(arch, process_host_arch, false, + if (platform_sp->IsCompatibleArchitecture(arch, process_host_arch, + ArchSpec::CompatibleMatch, platform_arch_ptr)) return platform_sp; } @@ -1984,8 +2003,9 @@ PlatformSP PlatformList::GetOrCreate(const ArchSpec &arch, (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); ++idx) { PlatformSP platform_sp = create_callback(false, &arch); - if (platform_sp && platform_sp->IsCompatibleArchitecture( - arch, process_host_arch, true, platform_arch_ptr)) { + if (platform_sp && + platform_sp->IsCompatibleArchitecture( + arch, process_host_arch, ArchSpec::ExactMatch, platform_arch_ptr)) { m_platforms.push_back(platform_sp); return platform_sp; } @@ -1996,7 +2016,8 @@ PlatformSP PlatformList::GetOrCreate(const ArchSpec &arch, ++idx) { PlatformSP platform_sp = create_callback(false, &arch); if (platform_sp && platform_sp->IsCompatibleArchitecture( - arch, process_host_arch, false, platform_arch_ptr)) { + arch, process_host_arch, ArchSpec::CompatibleMatch, + platform_arch_ptr)) { m_platforms.push_back(platform_sp); return platform_sp; } @@ -2030,7 +2051,7 @@ PlatformSP PlatformList::GetOrCreate(llvm::ArrayRef<ArchSpec> archs, if (m_selected_platform_sp) { for (const ArchSpec &arch : archs) { if (m_selected_platform_sp->IsCompatibleArchitecture( - arch, process_host_arch, false, nullptr)) + arch, process_host_arch, ArchSpec::CompatibleMatch, nullptr)) return m_selected_platform_sp; } } @@ -2038,8 +2059,8 @@ PlatformSP PlatformList::GetOrCreate(llvm::ArrayRef<ArchSpec> archs, // Prefer the host platform if it matches at least one architecture. if (host_platform_sp) { for (const ArchSpec &arch : archs) { - if (host_platform_sp->IsCompatibleArchitecture(arch, process_host_arch, - false, nullptr)) + if (host_platform_sp->IsCompatibleArchitecture( + arch, process_host_arch, ArchSpec::CompatibleMatch, nullptr)) return host_platform_sp; } } @@ -2054,10 +2075,9 @@ PlatformSP PlatformList::GetOrCreate(llvm::ArrayRef<ArchSpec> archs, // the same platform supports all architectures then that's the obvious next // best thing. if (candidates.size() == archs.size()) { - if (std::all_of(candidates.begin(), candidates.end(), - [&](const PlatformSP &p) -> bool { - return p->GetName() == candidates.front()->GetName(); - })) { + if (llvm::all_of(candidates, [&](const PlatformSP &p) -> bool { + return p->GetName() == candidates.front()->GetName(); + })) { return candidates.front(); } } @@ -2074,3 +2094,21 @@ PlatformSP PlatformList::Create(llvm::StringRef name) { m_platforms.push_back(platform_sp); return platform_sp; } + +bool PlatformList::LoadPlatformBinaryAndSetup(Process *process, + lldb::addr_t addr, bool notify) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + + PlatformCreateInstance create_callback; + for (int idx = 0; + (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); + ++idx) { + ArchSpec arch; + PlatformSP platform_sp = create_callback(true, &arch); + if (platform_sp) { + if (platform_sp->LoadPlatformBinaryAndSetup(process, addr, notify)) + return true; + } + } + return false; +} diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index b2f1318ca91d..e0cca0595ee3 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -9,6 +9,7 @@ #include <atomic> #include <memory> #include <mutex> +#include <optional> #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/ScopedPrinter.h" @@ -542,7 +543,7 @@ void Process::Finalize() { m_notifications.swap(empty_notifications); m_image_tokens.clear(); m_memory_cache.Clear(); - m_allocated_memory_cache.Clear(); + m_allocated_memory_cache.Clear(/*deallocate_memory=*/true); { std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); m_language_runtimes.clear(); @@ -1293,7 +1294,10 @@ uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) { } StateType Process::GetState() { - return m_public_state.GetValue(); + if (CurrentThreadIsPrivateStateThread()) + return m_private_state.GetValue(); + else + return m_public_state.GetValue(); } void Process::SetPublicState(StateType new_state, bool restarted) { @@ -1369,8 +1373,8 @@ Status Process::ResumeSynchronous(Stream *stream) { Status error = PrivateResume(); if (error.Success()) { - StateType state = WaitForProcessToStop(llvm::None, nullptr, true, - listener_sp, stream); + StateType state = + WaitForProcessToStop(std::nullopt, nullptr, true, listener_sp, stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) @@ -2633,7 +2637,7 @@ Status Process::LoadCore() { // Wait for a stopped event since we just posted one above... lldb::EventSP event_sp; StateType state = - WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp); + WaitForProcessToStop(std::nullopt, &event_sp, true, listener_sp); if (!StateIsStoppedState(state, false)) { Log *log = GetLog(LLDBLog::Process); @@ -2653,6 +2657,10 @@ DynamicLoader *Process::GetDynamicLoader() { return m_dyld_up.get(); } +void Process::SetDynamicLoader(DynamicLoaderUP dyld_up) { + m_dyld_up = std::move(dyld_up); +} + DataExtractor Process::GetAuxvData() { return DataExtractor(); } llvm::Expected<bool> Process::SaveCore(llvm::StringRef outfile) { @@ -2756,6 +2764,19 @@ ListenerSP ProcessAttachInfo::GetListenerForProcess(Debugger &debugger) { return debugger.GetListener(); } +Status Process::WillLaunch(Module *module) { + return DoWillLaunch(module); +} + +Status Process::WillAttachToProcessWithID(lldb::pid_t pid) { + return DoWillAttachToProcessWithID(pid); +} + +Status Process::WillAttachToProcessWithName(const char *process_name, + bool wait_for_launch) { + return DoWillAttachToProcessWithName(process_name, wait_for_launch); +} + Status Process::Attach(ProcessAttachInfo &attach_info) { m_abi_sp.reset(); m_process_input_reader.reset(); @@ -2905,9 +2926,9 @@ void Process::CompleteAttach() { ArchSpec process_host_arch = GetSystemArchitecture(); if (platform_sp) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); - if (target_arch.IsValid() && - !platform_sp->IsCompatibleArchitecture(target_arch, process_host_arch, - false, nullptr)) { + if (target_arch.IsValid() && !platform_sp->IsCompatibleArchitecture( + target_arch, process_host_arch, + ArchSpec::CompatibleMatch, nullptr)) { ArchSpec platform_arch; platform_sp = GetTarget().GetDebugger().GetPlatformList().GetOrCreate( target_arch, process_host_arch, &platform_arch); @@ -3018,7 +3039,7 @@ Status Process::ConnectRemote(llvm::StringRef remote_url) { if (error.Success()) { if (GetID() != LLDB_INVALID_PROCESS_ID) { EventSP event_sp; - StateType state = WaitForProcessStopPrivate(event_sp, llvm::None); + StateType state = WaitForProcessStopPrivate(event_sp, std::nullopt); if (state == eStateStopped || state == eStateCrashed) { // If we attached and actually have a process on the other end, then @@ -3362,7 +3383,7 @@ bool Process::ShouldBroadcastEvent(Event *event_ptr) { m_stdio_communication.Disconnect(); m_stdin_forward = false; - LLVM_FALLTHROUGH; + [[fallthrough]]; case eStateConnected: case eStateAttaching: case eStateLaunching: @@ -3765,7 +3786,7 @@ thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) { bool interrupt_requested = false; while (!exit_now) { EventSP event_sp; - GetEventsPrivate(event_sp, llvm::None, control_only); + GetEventsPrivate(event_sp, std::nullopt, control_only); if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) { LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 @@ -4589,12 +4610,12 @@ GetExpressionTimeout(const EvaluateExpressionOptions &options, return GetOneThreadExpressionTimeout(options); if (!options.GetTimeout()) - return llvm::None; + return std::nullopt; else return *options.GetTimeout() - GetOneThreadExpressionTimeout(options); } -static llvm::Optional<ExpressionResults> +static std::optional<ExpressionResults> HandleStoppedEvent(lldb::tid_t thread_id, const ThreadPlanSP &thread_plan_sp, RestorePlanState &restorer, const EventSP &event_sp, EventSP &event_to_broadcast_sp, @@ -4643,7 +4664,7 @@ HandleStoppedEvent(lldb::tid_t thread_id, const ThreadPlanSP &thread_plan_sp, if (!handle_interrupts && Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get())) - return llvm::None; + return std::nullopt; LLDB_LOG(log, "thread plan did not successfully complete"); if (!options.DoesUnwindOnError()) @@ -5636,7 +5657,9 @@ void Process::DidExec() { m_dyld_up.reset(); m_jit_loaders_up.reset(); m_image_tokens.clear(); - m_allocated_memory_cache.Clear(); + // After an exec, the inferior is a new process and these memory regions are + // no longer allocated. + m_allocated_memory_cache.Clear(/*deallocte_memory=*/false); { std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex); m_language_runtimes.clear(); @@ -6052,8 +6075,11 @@ bool Process::CallVoidArgVoidPtrReturn(const Address *address, llvm::consumeError(type_system_or_err.takeError()); return false; } + auto ts = *type_system_or_err; + if (!ts) + return false; CompilerType void_ptr_type = - type_system_or_err->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); + ts->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction( *thread, *address, void_ptr_type, llvm::ArrayRef<addr_t>(), options)); if (call_plan_sp) { diff --git a/lldb/source/Target/ProcessTrace.cpp b/lldb/source/Target/ProcessTrace.cpp index 6b147cb285a7..061af9e0e520 100644 --- a/lldb/source/Target/ProcessTrace.cpp +++ b/lldb/source/Target/ProcessTrace.cpp @@ -63,7 +63,7 @@ void ProcessTrace::DidAttach(ArchSpec &process_arch) { SetPrivateState(eStateStopped); EventSP event_sp; - WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp); + WaitForProcessToStop(std::nullopt, &event_sp, true, listener_sp); RestoreProcessEvents(); diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp index 7364660650e8..ee344b01c6b8 100644 --- a/lldb/source/Target/RegisterContext.cpp +++ b/lldb/source/Target/RegisterContext.cpp @@ -357,7 +357,7 @@ Status RegisterContext::ReadRegisterValueFromMemory( // TODO: we might need to add a parameter to this function in case the byte // order of the memory data doesn't match the process. For now we are // assuming they are the same. - reg_value.SetFromMemoryData(reg_info, src, src_len, + reg_value.SetFromMemoryData(*reg_info, src, src_len, process_sp->GetByteOrder(), error); } else error.SetErrorString("invalid process"); @@ -368,37 +368,41 @@ Status RegisterContext::ReadRegisterValueFromMemory( Status RegisterContext::WriteRegisterValueToMemory( const RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue ®_value) { - uint8_t dst[RegisterValue::kMaxRegisterByteSize]; - Status error; - ProcessSP process_sp(m_thread.GetProcess()); - if (process_sp) { - // TODO: we might need to add a parameter to this function in case the byte - // order of the memory data doesn't match the process. For now we are - // assuming they are the same. + if (!process_sp) { + error.SetErrorString("invalid process"); + return error; + } - const uint32_t bytes_copied = reg_value.GetAsMemoryData( - reg_info, dst, dst_len, process_sp->GetByteOrder(), error); + if (reg_info == nullptr) { + error.SetErrorString("Invalid register info argument."); + return error; + } - if (error.Success()) { - if (bytes_copied == 0) { - error.SetErrorString("byte copy failed."); - } else { - const uint32_t bytes_written = - process_sp->WriteMemory(dst_addr, dst, bytes_copied, error); - if (bytes_written != bytes_copied) { - if (error.Success()) { - // This might happen if we read _some_ bytes but not all - error.SetErrorStringWithFormat("only wrote %u of %u bytes", - bytes_written, bytes_copied); - } + // TODO: we might need to add a parameter to this function in case the byte + // order of the memory data doesn't match the process. For now we are + // assuming they are the same. + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + const uint32_t bytes_copied = reg_value.GetAsMemoryData( + *reg_info, dst, dst_len, process_sp->GetByteOrder(), error); + + if (error.Success()) { + if (bytes_copied == 0) { + error.SetErrorString("byte copy failed."); + } else { + const uint32_t bytes_written = + process_sp->WriteMemory(dst_addr, dst, bytes_copied, error); + if (bytes_written != bytes_copied) { + if (error.Success()) { + // This might happen if we read _some_ bytes but not all + error.SetErrorStringWithFormat("only wrote %u of %u bytes", + bytes_written, bytes_copied); } } } - } else - error.SetErrorString("invalid process"); + } return error; } diff --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp index c7b0ade845cf..0bd6c9251c85 100644 --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -14,6 +14,7 @@ #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Utility/StreamString.h" +#include <optional> using namespace lldb_private; using namespace lldb; @@ -305,16 +306,16 @@ bool RemoteAwarePlatform::GetRemoteOSVersion() { return false; } -llvm::Optional<std::string> RemoteAwarePlatform::GetRemoteOSBuildString() { +std::optional<std::string> RemoteAwarePlatform::GetRemoteOSBuildString() { if (m_remote_platform_sp) return m_remote_platform_sp->GetRemoteOSBuildString(); - return llvm::None; + return std::nullopt; } -llvm::Optional<std::string> RemoteAwarePlatform::GetRemoteOSKernelDescription() { +std::optional<std::string> RemoteAwarePlatform::GetRemoteOSKernelDescription() { if (m_remote_platform_sp) return m_remote_platform_sp->GetRemoteOSKernelDescription(); - return llvm::None; + return std::nullopt; } ArchSpec RemoteAwarePlatform::GetRemoteSystemArchitecture() { diff --git a/lldb/source/Target/SectionLoadList.cpp b/lldb/source/Target/SectionLoadList.cpp index 03160ee10dd8..d4bf0573b229 100644 --- a/lldb/source/Target/SectionLoadList.cpp +++ b/lldb/source/Target/SectionLoadList.cpp @@ -106,8 +106,8 @@ bool SectionLoadList::SetSectionLoadAddress(const lldb::SectionSP §ion, ModuleSP curr_module_sp(ats_pos->second->GetModule()); if (curr_module_sp) { module_sp->ReportWarning( - "address 0x%16.16" PRIx64 - " maps to more than one section: %s.%s and %s.%s", + "address {0:x16} maps to more than one section: {1}.{2} and " + "{3}.{4}", load_addr, module_sp->GetFileSpec().GetFilename().GetCString(), section->GetName().GetCString(), curr_module_sp->GetFileSpec().GetFilename().GetCString(), @@ -116,8 +116,18 @@ bool SectionLoadList::SetSectionLoadAddress(const lldb::SectionSP §ion, } } ats_pos->second = section; - } else + } else { + // Remove the old address->section entry, if + // there is one. + for (const auto &entry : m_addr_to_sect) { + if (entry.second == section) { + const auto &it_pos = m_addr_to_sect.find(entry.first); + m_addr_to_sect.erase(it_pos); + break; + } + } m_addr_to_sect[load_addr] = section; + } return true; // Changed } else { diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 4fb5ba0b735e..c04b58e80523 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -20,6 +20,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContextScope.h" +#include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" @@ -43,7 +44,7 @@ using namespace lldb_private; // The first bits in the flags are reserved for the SymbolContext::Scope bits // so we know if we have tried to look up information in our internal symbol // context (m_sc) already. -#define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1)) +#define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextLastItem) << 1) #define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1) #define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1) #define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1) @@ -420,10 +421,12 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) { return m_sc; } -VariableList *StackFrame::GetVariableList(bool get_file_globals) { +VariableList *StackFrame::GetVariableList(bool get_file_globals, + Status *error_ptr) { std::lock_guard<std::recursive_mutex> guard(m_mutex); if (m_flags.IsClear(RESOLVED_VARIABLES)) { m_flags.Set(RESOLVED_VARIABLES); + m_variable_list_sp = std::make_shared<VariableList>(); Block *frame_block = GetFrameBlock(); @@ -431,7 +434,6 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals) { const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; - m_variable_list_sp = std::make_shared<VariableList>(); frame_block->AppendBlockVariables(can_create, get_child_variables, stop_if_child_block_is_inlined_function, [](Variable *v) { return true; }, @@ -455,6 +457,17 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals) { } } + if (error_ptr && m_variable_list_sp->GetSize() == 0) { + // Check with the symbol file to check if there is an error for why we + // don't have variables that the user might need to know about. + GetSymbolContext(eSymbolContextEverything); + if (m_sc.module_sp) { + SymbolFile *sym_file = m_sc.module_sp->GetSymbolFile(); + if (sym_file) + *error_ptr = sym_file->GetFrameVariableError(*this); + } + } + return m_variable_list_sp.get(); } @@ -666,7 +679,7 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath( } var_expr = var_expr.drop_front(); // Remove the '-' - LLVM_FALLTHROUGH; + [[fallthrough]]; case '.': { var_expr = var_expr.drop_front(); // Remove the '.' or '>' separator_idx = var_expr.find_first_of(".-["); @@ -1147,16 +1160,16 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { ValueObjectSP valobj_sp; { // Scope for stack frame mutex. We need to drop this mutex before we figure - // out the dynamic value. That will require converting the StackID in the - // VO back to a StackFrame, which will in turn require locking the - // StackFrameList. If we still hold the StackFrame mutex, we could suffer - // lock inversion against the pattern of getting the StackFrameList and + // out the dynamic value. That will require converting the StackID in the + // VO back to a StackFrame, which will in turn require locking the + // StackFrameList. If we still hold the StackFrame mutex, we could suffer + // lock inversion against the pattern of getting the StackFrameList and // then the stack frame, which is fairly common. std::lock_guard<std::recursive_mutex> guard(m_mutex); if (IsHistorical()) { return valobj_sp; } - VariableList *var_list = GetVariableList(true); + VariableList *var_list = GetVariableList(true, nullptr); if (var_list) { // Make sure the variable is a frame variable const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); @@ -1355,9 +1368,11 @@ lldb::ValueObjectSP StackFrame::GuessValueForAddress(lldb::addr_t addr) { "Unable to guess value for given address"); return ValueObjectSP(); } else { + auto ts = *c_type_system_or_err; + if (!ts) + return {}; CompilerType void_ptr_type = - c_type_system_or_err - ->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar) + ts->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar) .GetPointerType(); return ValueObjectMemory::Create(this, "", addr, void_ptr_type); } @@ -1698,7 +1713,7 @@ lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, } const bool get_file_globals = false; - VariableList *variables = GetVariableList(get_file_globals); + VariableList *variables = GetVariableList(get_file_globals, nullptr); if (!variables) { return ValueObjectSP(); @@ -1932,12 +1947,12 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, case Debugger::eStopDisassemblyTypeNoDebugInfo: if (have_debuginfo) break; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Debugger::eStopDisassemblyTypeNoSource: if (have_source) break; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Debugger::eStopDisassemblyTypeAlways: if (target) { diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index 14663e4b7c7b..c782b506a9cb 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -168,7 +168,7 @@ void StackFrameList::ResetCurrentInlinedDepth() { break; } } - LLVM_FALLTHROUGH; + [[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 diff --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp index 73d22d5bb4e6..0ccb1ae9c031 100644 --- a/lldb/source/Target/StackFrameRecognizer.cpp +++ b/lldb/source/Target/StackFrameRecognizer.cpp @@ -77,7 +77,7 @@ void StackFrameRecognizerManager::ForEach( symbol_name = entry.symbol_regexp->GetText().str(); callback(entry.recognizer_id, entry.recognizer->GetName(), module_name, - llvm::makeArrayRef(ConstString(symbol_name)), true); + llvm::ArrayRef(ConstString(symbol_name)), true); } else { callback(entry.recognizer_id, entry.recognizer->GetName(), diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp index b8ad25e71f06..c739ac7058ca 100644 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -63,6 +63,10 @@ json::Value ModuleStats::ToJSON() const { module.try_emplace("debugInfoIndexSavedToCache", debug_info_index_saved_to_cache); module.try_emplace("debugInfoEnabled", debug_info_enabled); + module.try_emplace("debugInfoHadVariableErrors", + debug_info_had_variable_errors); + module.try_emplace("debugInfoHadIncompleteTypes", + debug_info_had_incomplete_types); module.try_emplace("symbolTableStripped", symtab_stripped); if (!symfile_path.empty()) module.try_emplace("symbolFilePath", symfile_path); @@ -73,6 +77,17 @@ json::Value ModuleStats::ToJSON() const { symfile_ids.emplace_back(symfile_id); module.try_emplace("symbolFileModuleIdentifiers", std::move(symfile_ids)); } + + if (!type_system_stats.empty()) { + json::Array type_systems; + for (const auto &entry : type_system_stats) { + json::Object obj; + obj.try_emplace(entry.first().str(), entry.second); + type_systems.emplace_back(std::move(obj)); + } + module.try_emplace("typeSystemInfo", std::move(type_systems)); + } + return module; } @@ -136,13 +151,14 @@ json::Value TargetStats::ToJSON(Target &target) { target_metrics_json.try_emplace("breakpoints", std::move(breakpoints_array)); target_metrics_json.try_emplace("totalBreakpointResolveTime", totalBreakpointResolveTime); + target_metrics_json.try_emplace("sourceMapDeduceCount", m_source_map_deduce_count); return target_metrics_json; } void TargetStats::SetLaunchOrAttachTime() { m_launch_or_attach_time = StatsClock::now(); - m_first_private_stop_time = llvm::None; + m_first_private_stop_time = std::nullopt; } void TargetStats::SetFirstPrivateStopTime() { @@ -161,6 +177,10 @@ void TargetStats::SetFirstPublicStopTime() { m_first_public_stop_time = StatsClock::now(); } +void TargetStats::IncreaseSourceMapDeduceCount() { + ++m_source_map_deduce_count; +} + bool DebuggerStats::g_collecting_stats = false; llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger, @@ -188,6 +208,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger, const uint64_t num_modules = Module::GetNumberAllocatedModules(); uint32_t num_debug_info_enabled_modules = 0; uint32_t num_modules_has_debug_info = 0; + uint32_t num_modules_with_variable_errors = 0; + uint32_t num_modules_with_incomplete_types = 0; uint32_t num_stripped_modules = 0; for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { Module *module = Module::GetAllocatedModuleAtIndex(image_idx); @@ -237,16 +259,30 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger, ++num_stripped_modules; module_stat.debug_info_enabled = sym_file->GetLoadDebugInfoEnabled() && module_stat.debug_info_size > 0; + module_stat.debug_info_had_variable_errors = + sym_file->GetDebugInfoHadFrameVariableErrors(); if (module_stat.debug_info_enabled) ++num_debug_info_enabled_modules; if (module_stat.debug_info_size > 0) ++num_modules_has_debug_info; + if (module_stat.debug_info_had_variable_errors) + ++num_modules_with_variable_errors; } symtab_parse_time += module_stat.symtab_parse_time; symtab_index_time += module_stat.symtab_index_time; debug_parse_time += module_stat.debug_parse_time; debug_index_time += module_stat.debug_index_time; debug_info_size += module_stat.debug_info_size; + module->ForEachTypeSystem([&](lldb::TypeSystemSP ts) { + if (auto stats = ts->ReportStatistics()) + module_stat.type_system_stats.insert({ts->GetPluginName(), *stats}); + if (ts->GetHasForcefullyCompletedTypes()) + module_stat.debug_info_had_incomplete_types = true; + return true; + }); + if (module_stat.debug_info_had_incomplete_types) + ++num_modules_with_incomplete_types; + json_modules.emplace_back(module_stat.ToJSON()); } @@ -270,6 +306,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger, {"totalDebugInfoByteSize", debug_info_size}, {"totalModuleCount", num_modules}, {"totalModuleCountHasDebugInfo", num_modules_has_debug_info}, + {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors}, + {"totalModuleCountWithIncompleteTypes", num_modules_with_incomplete_types}, {"totalDebugInfoEnabled", num_debug_info_enabled_modules}, {"totalSymbolTableStripped", num_stripped_modules}, }; diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 00d30070c8c9..225234c0ffba 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -20,6 +20,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" @@ -361,29 +362,21 @@ protected: " not running commands to avoid recursion."); bool ignoring_breakpoints = process->GetIgnoreBreakpointsInExpressions(); - if (ignoring_breakpoints) { - m_should_stop = false; - // Internal breakpoints will always stop. - for (size_t j = 0; j < num_owners; j++) { - lldb::BreakpointLocationSP bp_loc_sp = - bp_site_sp->GetOwnerAtIndex(j); - if (bp_loc_sp->GetBreakpoint().IsInternal()) { - m_should_stop = true; - break; - } - } - } else { - m_should_stop = true; + // Internal breakpoints should be allowed to do their job, we + // can make sure they don't do anything that would cause recursive + // command execution: + if (!m_was_all_internal) { + m_should_stop = !ignoring_breakpoints; + LLDB_LOGF(log, + "StopInfoBreakpoint::PerformAction - in expression, " + "continuing: %s.", + m_should_stop ? "true" : "false"); + Debugger::ReportWarning( + "hit breakpoint while running function, skipping commands " + "and conditions to prevent recursion", + process->GetTarget().GetDebugger().GetID()); + return; } - LLDB_LOGF(log, - "StopInfoBreakpoint::PerformAction - in expression, " - "continuing: %s.", - m_should_stop ? "true" : "false"); - Debugger::ReportWarning( - "hit breakpoint while running function, skipping commands and " - "conditions to prevent recursion", - process->GetTarget().GetDebugger().GetID()); - return; } StoppointCallbackContext context(event_ptr, exe_ctx, false); @@ -690,39 +683,184 @@ public: } protected: + using StopInfoWatchpointSP = std::shared_ptr<StopInfoWatchpoint>; + // This plan is used to orchestrate stepping over the watchpoint for + // architectures (e.g. ARM) that report the watch before running the watched + // access. This is the sort of job you have to defer to the thread plans, + // if you try to do it directly in the stop info and there are other threads + // that needed to process this stop you will have yanked control away from + // them and they won't behave correctly. + class ThreadPlanStepOverWatchpoint : public ThreadPlanStepInstruction { + public: + ThreadPlanStepOverWatchpoint(Thread &thread, + StopInfoWatchpointSP stop_info_sp, + WatchpointSP watch_sp) + : ThreadPlanStepInstruction(thread, false, true, eVoteNoOpinion, + eVoteNoOpinion), + m_stop_info_sp(stop_info_sp), m_watch_sp(watch_sp) { + assert(watch_sp); + m_watch_index = watch_sp->GetHardwareIndex(); + } + + bool DoWillResume(lldb::StateType resume_state, + bool current_plan) override { + if (resume_state == eStateSuspended) + return true; + + if (!m_did_disable_wp) { + GetThread().GetProcess()->DisableWatchpoint(m_watch_sp.get(), false); + m_did_disable_wp = true; + } + return true; + } + + bool DoPlanExplainsStop(Event *event_ptr) override { + if (ThreadPlanStepInstruction::DoPlanExplainsStop(event_ptr)) + return true; + StopInfoSP stop_info_sp = GetThread().GetPrivateStopInfo(); + // lldb-server resets the stop info for threads that didn't get to run, + // so we might have not gotten to run, but still have a watchpoint stop + // reason, in which case this will indeed be for us. + if (stop_info_sp + && stop_info_sp->GetStopReason() == eStopReasonWatchpoint) + return true; + return false; + } + + void DidPop() override { + // Don't artifically keep the watchpoint alive. + m_watch_sp.reset(); + } + + bool ShouldStop(Event *event_ptr) override { + bool should_stop = ThreadPlanStepInstruction::ShouldStop(event_ptr); + bool plan_done = MischiefManaged(); + if (plan_done) { + m_stop_info_sp->SetStepOverPlanComplete(); + GetThread().SetStopInfo(m_stop_info_sp); + ResetWatchpoint(); + } + return should_stop; + } + + bool ShouldRunBeforePublicStop() override { + return true; + } + + protected: + void ResetWatchpoint() { + if (!m_did_disable_wp) + return; + m_did_disable_wp = true; + GetThread().GetProcess()->EnableWatchpoint(m_watch_sp.get(), true); + m_watch_sp->SetHardwareIndex(m_watch_index); + } + + private: + StopInfoWatchpointSP m_stop_info_sp; + WatchpointSP m_watch_sp; + uint32_t m_watch_index = LLDB_INVALID_INDEX32; + bool m_did_disable_wp = false; + }; + bool ShouldStopSynchronous(Event *event_ptr) override { - // ShouldStop() method is idempotent and should not affect hit count. See - // Process::RunPrivateStateThread()->Process()->HandlePrivateEvent() - // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()-> - // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()-> - // StopInfoWatchpoint::ShouldStop() and - // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()-> - // StopInfoWatchpoint::PerformAction(). + // If we are running our step-over the watchpoint plan, stop if it's done + // and continue if it's not: if (m_should_stop_is_valid) return m_should_stop; + // If we are running our step over plan, then stop here and let the regular + // ShouldStop figure out what we should do: Otherwise, give our plan + // more time to get run: + if (m_using_step_over_plan) + return m_step_over_plan_complete; + + Log *log = GetLog(LLDBLog::Process); ThreadSP thread_sp(m_thread_wp.lock()); - if (thread_sp) { - WatchpointSP wp_sp( - thread_sp->CalculateTarget()->GetWatchpointList().FindByID( - GetValue())); - if (wp_sp) { - // Check if we should stop at a watchpoint. - ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); - StoppointCallbackContext context(event_ptr, exe_ctx, true); - m_should_stop = wp_sp->ShouldStop(&context); - } else { - Log *log = GetLog(LLDBLog::Process); + assert(thread_sp); + + if (thread_sp->GetTemporaryResumeState() == eStateSuspended) { + // This is the second firing of a watchpoint so don't process it again. + LLDB_LOG(log, "We didn't run but stopped with a StopInfoWatchpoint, we " + "have already handled this one, don't do it again."); + m_should_stop = false; + m_should_stop_is_valid = true; + return m_should_stop; + } + + WatchpointSP wp_sp( + thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue())); + // If we can no longer find the watchpoint, we just have to stop: + if (!wp_sp) { - LLDB_LOGF(log, - "Process::%s could not find watchpoint location id: %" PRId64 - "...", - __FUNCTION__, GetValue()); + LLDB_LOGF(log, + "Process::%s could not find watchpoint location id: %" PRId64 + "...", + __FUNCTION__, GetValue()); + + m_should_stop = true; + m_should_stop_is_valid = true; + return true; + } + + ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); + StoppointCallbackContext context(event_ptr, exe_ctx, true); + m_should_stop = wp_sp->ShouldStop(&context); + if (!m_should_stop) { + // This won't happen at present because we only allow one watchpoint per + // watched range. So we won't stop at a watched address with a disabled + // watchpoint. If we start allowing overlapping watchpoints, then we + // will have to make watchpoints be real "WatchpointSite" and delegate to + // all the watchpoints sharing the site. In that case, the code below + // would be the right thing to do. + m_should_stop_is_valid = true; + return m_should_stop; + } + // If this is a system where we need to execute the watchpoint by hand + // after the hit, queue a thread plan to do that, and then say not to stop. + // Otherwise, let the async action figure out whether the watchpoint should + // stop + + ProcessSP process_sp = exe_ctx.GetProcessSP(); + uint32_t num; + bool wp_triggers_after; + + if (!process_sp->GetWatchpointSupportInfo(num, wp_triggers_after) + .Success()) { + m_should_stop_is_valid = true; + m_should_stop = true; + return m_should_stop; + } + + if (!wp_triggers_after) { + // We have to step over the watchpoint before we know what to do: + StopInfoWatchpointSP me_as_siwp_sp + = std::static_pointer_cast<StopInfoWatchpoint>(shared_from_this()); + ThreadPlanSP step_over_wp_sp(new ThreadPlanStepOverWatchpoint( + *(thread_sp.get()), me_as_siwp_sp, wp_sp)); + Status error; + error = thread_sp->QueueThreadPlan(step_over_wp_sp, false); + // If we couldn't push the thread plan, just stop here: + if (!error.Success()) { + LLDB_LOGF(log, "Could not push our step over watchpoint plan: %s", + error.AsCString()); m_should_stop = true; + m_should_stop_is_valid = true; + return true; + } else { + // Otherwise, don't set m_should_stop, we don't know that yet. Just + // say we should continue, and tell the thread we really should do so: + thread_sp->SetShouldRunBeforePublicStop(true); + m_using_step_over_plan = true; + return false; } + } else { + // We didn't have to do anything special + m_should_stop_is_valid = true; + return m_should_stop; } - m_should_stop_is_valid = true; + return m_should_stop; } @@ -749,57 +887,12 @@ protected: thread_sp->CalculateTarget()->GetWatchpointList().FindByID( GetValue())); if (wp_sp) { - ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); - ProcessSP process_sp = exe_ctx.GetProcessSP(); - - { - // check if this process is running on an architecture where - // watchpoints trigger before the associated instruction runs. if so, - // disable the WP, single-step and then re-enable the watchpoint - if (process_sp) { - uint32_t num; - bool wp_triggers_after; - - if (process_sp->GetWatchpointSupportInfo(num, wp_triggers_after) - .Success()) { - if (!wp_triggers_after) { - // We need to preserve the watch_index before watchpoint is - // disable. Since Watchpoint::SetEnabled will clear the watch - // index. This will fix TestWatchpointIter failure - Watchpoint *wp = wp_sp.get(); - uint32_t watch_index = wp->GetHardwareIndex(); - process_sp->DisableWatchpoint(wp, false); - 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_status)); - if (new_plan_sp && new_plan_status.Success()) { - new_plan_sp->SetIsControllingPlan(true); - new_plan_sp->SetOkayToDiscard(false); - new_plan_sp->SetPrivate(true); - } - process_sp->GetThreadList().SetSelectedThreadByID( - thread_sp->GetID()); - process_sp->ResumeSynchronous(nullptr); - process_sp->GetThreadList().SetSelectedThreadByID( - thread_sp->GetID()); - thread_sp->SetStopInfo(stored_stop_info_sp); - process_sp->EnableWatchpoint(wp, false); - wp->SetHardwareIndex(watch_index); - } - } - } - } - // This sentry object makes sure the current watchpoint is disabled // while performing watchpoint actions, and it is then enabled after we // are finished. + ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); + ProcessSP process_sp = exe_ctx.GetProcessSP(); + WatchpointSentry sentry(process_sp, wp_sp); /* @@ -825,18 +918,10 @@ protected: } } - // TODO: This condition should be checked in the synchronous part of the - // watchpoint code - // (Watchpoint::ShouldStop), so that we avoid pulling an event even if - // the watchpoint fails the ignore count condition. It is moved here - // temporarily, because for archs with - // watchpoint_exceptions_received=before, the code in the previous - // lines takes care of moving the inferior to next PC. We have to check - // the ignore count condition after this is done, otherwise we will hit - // same watchpoint multiple times until we pass ignore condition, but - // we won't actually be ignoring them. - if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount()) + if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount()) { m_should_stop = false; + m_should_stop_is_valid = true; + } Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); @@ -859,10 +944,9 @@ protected: Scalar scalar_value; if (result_value_sp->ResolveValue(scalar_value)) { if (scalar_value.ULongLong(1) == 0) { - // We have been vetoed. This takes precedence over querying - // the watchpoint whether it should stop (aka ignore count - // and friends). See also StopInfoWatchpoint::ShouldStop() - // as well as Process::ProcessEventData::DoOnRemoval(). + // The condition failed, which we consider "not having hit + // the watchpoint" so undo the hit count here. + wp_sp->UndoHitCount(); m_should_stop = false; } else m_should_stop = true; @@ -946,9 +1030,16 @@ protected: } private: + void SetStepOverPlanComplete() { + assert(m_using_step_over_plan); + m_step_over_plan_complete = true; + } + bool m_should_stop = false; bool m_should_stop_is_valid = false; lldb::addr_t m_watch_hit_addr; + bool m_step_over_plan_complete = false; + bool m_using_step_over_plan = false; }; // StopInfoUnixSignal diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index f16fc6b5da85..fd0cf0a5361d 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -26,6 +26,7 @@ #include "lldb/Core/StreamFile.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/ExpressionVariable.h" #include "lldb/Expression/REPL.h" @@ -66,6 +67,7 @@ #include <memory> #include <mutex> +#include <optional> using namespace lldb; using namespace lldb_private; @@ -107,7 +109,7 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed"); SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded"); - + CheckInWithManager(); LLDB_LOG(GetLog(LLDBLog::Object), "{0} Target::Target()", @@ -140,10 +142,8 @@ void Target::PrimeFromDummyTarget(Target &target) { AddBreakpoint(std::move(new_bp), false); } - for (auto bp_name_entry : target.m_breakpoint_names) { - - BreakpointName *new_bp_name = new BreakpointName(*bp_name_entry.second); - AddBreakpointName(new_bp_name); + for (const auto &bp_name_entry : target.m_breakpoint_names) { + AddBreakpointName(std::make_unique<BreakpointName>(*bp_name_entry.second)); } m_frame_recognizer_manager_up = std::make_unique<StackFrameRecognizerManager>( @@ -177,6 +177,7 @@ void Target::CleanupProcess() { // clean up needs some help from the process. m_breakpoint_list.ClearAllBreakpointSites(); m_internal_breakpoint_list.ClearAllBreakpointSites(); + ResetBreakpointHitCounts(); // Disable watchpoints just on the debugger side. std::unique_lock<std::recursive_mutex> lock; this->GetWatchpointList().GetListMutex(lock); @@ -284,6 +285,7 @@ void Target::Destroy() { m_breakpoint_list.RemoveAll(notify); m_internal_breakpoint_list.RemoveAll(notify); m_last_created_breakpoint.reset(); + m_watchpoint_list.RemoveAll(notify); m_last_created_watchpoint.reset(); m_search_filter_sp.reset(); m_image_search_paths.Clear(notify); @@ -355,7 +357,9 @@ BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, bool hardware, LazyBool move_to_nearest_code) { FileSpec remapped_file; - if (!GetSourcePathMap().ReverseRemapPath(file, remapped_file)) + std::optional<llvm::StringRef> removed_prefix_opt = + GetSourcePathMap().ReverseRemapPath(file, remapped_file); + if (!removed_prefix_opt) remapped_file = file; if (check_inlines == eLazyBoolCalculate) { @@ -399,7 +403,7 @@ BreakpointSP Target::CreateBreakpoint(const FileSpecList *containingModules, return nullptr; BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine( - nullptr, offset, skip_prologue, location_spec)); + nullptr, offset, skip_prologue, location_spec, removed_prefix_opt)); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } @@ -707,8 +711,9 @@ void Target::AddNameToBreakpoint(BreakpointSP &bp_sp, const char *name, bp_sp->AddName(name); } -void Target::AddBreakpointName(BreakpointName *bp_name) { - m_breakpoint_names.insert(std::make_pair(bp_name->GetName(), bp_name)); +void Target::AddBreakpointName(std::unique_ptr<BreakpointName> bp_name) { + m_breakpoint_names.insert( + std::make_pair(bp_name->GetName(), std::move(bp_name))); } BreakpointName *Target::FindBreakpointName(ConstString name, bool can_create, @@ -718,19 +723,20 @@ BreakpointName *Target::FindBreakpointName(ConstString name, bool can_create, return nullptr; BreakpointNameList::iterator iter = m_breakpoint_names.find(name); - if (iter == m_breakpoint_names.end()) { - if (!can_create) { - error.SetErrorStringWithFormat("Breakpoint name \"%s\" doesn't exist and " - "can_create is false.", - name.AsCString()); - return nullptr; - } + if (iter != m_breakpoint_names.end()) { + return iter->second.get(); + } - iter = m_breakpoint_names - .insert(std::make_pair(name, new BreakpointName(name))) - .first; + if (!can_create) { + error.SetErrorStringWithFormat("Breakpoint name \"%s\" doesn't exist and " + "can_create is false.", + name.AsCString()); + return nullptr; } - return (iter->second); + + return m_breakpoint_names + .insert(std::make_pair(name, std::make_unique<BreakpointName>(name))) + .first->second.get(); } void Target::DeleteBreakpointName(ConstString name) { @@ -773,8 +779,8 @@ void Target::ApplyNameToBreakpoints(BreakpointName &bp_name) { void Target::GetBreakpointNames(std::vector<std::string> &names) { names.clear(); - for (auto bp_name : m_breakpoint_names) { - names.push_back(bp_name.first.AsCString()); + for (const auto& bp_name_entry : m_breakpoint_names) { + names.push_back(bp_name_entry.first.AsCString()); } llvm::sort(names); } @@ -1002,6 +1008,10 @@ bool Target::EnableBreakpointByID(break_id_t break_id) { return false; } +void Target::ResetBreakpointHitCounts() { + GetBreakpointList().ResetHitCounts(); +} + Status Target::SerializeBreakpointsToFile(const FileSpec &file, const BreakpointIDList &bp_ids, bool append) { @@ -1427,7 +1437,8 @@ void Target::SetExecutableModule(ModuleSP &executable_sp, if (!m_arch.GetSpec().IsValid()) { m_arch = executable_sp->GetArchitecture(); LLDB_LOG(log, - "setting architecture to {0} ({1}) based on executable file", + "Target::SetExecutableModule setting architecture to {0} ({1}) " + "based on executable file", m_arch.GetSpec().GetArchitectureName(), m_arch.GetSpec().GetTriple().getTriple()); } @@ -1474,7 +1485,8 @@ void Target::SetExecutableModule(ModuleSP &executable_sp, } } -bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { +bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform, + bool merge) { Log *log = GetLog(LLDBLog::Target); bool missing_local_arch = !m_arch.GetSpec().IsValid(); bool replace_local_arch = true; @@ -1487,8 +1499,8 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { if (set_platform) { if (other.IsValid()) { auto platform_sp = GetPlatform(); - if (!platform_sp || - !platform_sp->IsCompatibleArchitecture(other, {}, false, nullptr)) { + if (!platform_sp || !platform_sp->IsCompatibleArchitecture( + other, {}, ArchSpec::CompatibleMatch, nullptr)) { ArchSpec platform_arch; if (PlatformSP arch_platform_sp = GetDebugger().GetPlatformList().GetOrCreate(other, {}, @@ -1502,7 +1514,7 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { } if (!missing_local_arch) { - if (m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { + if (merge && m_arch.GetSpec().IsCompatibleMatch(arch_spec)) { other.MergeFrom(m_arch.GetSpec()); if (m_arch.GetSpec().IsCompatibleMatch(other)) { @@ -1526,7 +1538,9 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { // specified if (replace_local_arch) m_arch = other; - LLDB_LOG(log, "set architecture to {0} ({1})", + LLDB_LOG(log, + "Target::SetArchitecture merging compatible arch; arch " + "is now {0} ({1})", m_arch.GetSpec().GetArchitectureName(), m_arch.GetSpec().GetTriple().getTriple()); return true; @@ -1534,9 +1548,13 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform) { // If we have an executable file, try to reset the executable to the desired // architecture - LLDB_LOGF(log, "Target::SetArchitecture changing architecture to %s (%s)", - arch_spec.GetArchitectureName(), - arch_spec.GetTriple().getTriple().c_str()); + LLDB_LOGF( + log, + "Target::SetArchitecture changing architecture to %s (%s) from %s (%s)", + arch_spec.GetArchitectureName(), + arch_spec.GetTriple().getTriple().c_str(), + m_arch.GetSpec().GetArchitectureName(), + m_arch.GetSpec().GetTriple().getTriple().c_str()); m_arch = other; ModuleSP executable_sp = GetExecutableModule(); @@ -1665,6 +1683,34 @@ void Target::ModulesDidUnload(ModuleList &module_list, bool delete_locations) { m_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); m_internal_breakpoint_list.UpdateBreakpoints(module_list, false, delete_locations); + + // If a module was torn down it will have torn down the 'TypeSystemClang's + // that we used as source 'ASTContext's for the persistent variables in + // the current target. Those would now be unsafe to access because the + // 'DeclOrigin' are now possibly stale. Thus clear all persistent + // variables. We only want to flush 'TypeSystem's if the module being + // unloaded was capable of describing a source type. JITted module unloads + // happen frequently for Objective-C utility functions or the REPL and rely + // on the persistent variables to stick around. + const bool should_flush_type_systems = + module_list.AnyOf([](lldb_private::Module &module) { + auto *object_file = module.GetObjectFile(); + + if (!object_file) + return false; + + auto type = object_file->GetType(); + + // eTypeExecutable: when debugged binary was rebuilt + // eTypeSharedLibrary: if dylib was re-loaded + return module.FileHasChanged() && + (type == ObjectFile::eTypeObjectFile || + type == ObjectFile::eTypeExecutable || + type == ObjectFile::eTypeSharedLibrary); + }); + + if (should_flush_type_systems) + m_scratch_type_system_map.Clear(); } } @@ -2085,11 +2131,12 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify, // a suitable image. if (m_image_search_paths.GetSize()) { ModuleSpec transformed_spec(module_spec); + ConstString transformed_dir; if (m_image_search_paths.RemapPath( - module_spec.GetFileSpec().GetDirectory(), - transformed_spec.GetFileSpec().GetDirectory())) { - transformed_spec.GetFileSpec().GetFilename() = - module_spec.GetFileSpec().GetFilename(); + module_spec.GetFileSpec().GetDirectory(), transformed_dir)) { + transformed_spec.GetFileSpec().SetDirectory(transformed_dir); + transformed_spec.GetFileSpec().SetFilename( + module_spec.GetFileSpec().GetFilename()); error = ModuleList::GetSharedModule(transformed_spec, module_sp, &search_paths, &old_modules, &did_create_module); @@ -2283,7 +2330,7 @@ void Target::ImageSearchPathsChanged(const PathMappingList &path_list, target->SetExecutableModule(exe_module_sp, eLoadDependentsYes); } -llvm::Expected<TypeSystem &> +llvm::Expected<lldb::TypeSystemSP> Target::GetScratchTypeSystemForLanguage(lldb::LanguageType language, bool create_on_demand) { if (!m_valid) @@ -2312,14 +2359,15 @@ Target::GetScratchTypeSystemForLanguage(lldb::LanguageType language, create_on_demand); } -std::vector<TypeSystem *> Target::GetScratchTypeSystems(bool create_on_demand) { +std::vector<lldb::TypeSystemSP> +Target::GetScratchTypeSystems(bool create_on_demand) { if (!m_valid) return {}; // Some TypeSystem instances are associated with several LanguageTypes so // they will show up several times in the loop below. The SetVector filters // out all duplicates as they serve no use for the caller. - llvm::SetVector<TypeSystem *> scratch_type_systems; + std::vector<lldb::TypeSystemSP> scratch_type_systems; LanguageSet languages_for_expressions = Language::GetLanguagesSupportingTypeSystemsForExpressions(); @@ -2334,10 +2382,17 @@ std::vector<TypeSystem *> Target::GetScratchTypeSystems(bool create_on_demand) { "system available", Language::GetNameForLanguageType(language)); else - scratch_type_systems.insert(&type_system_or_err.get()); + if (auto ts = *type_system_or_err) + scratch_type_systems.push_back(ts); } - - return scratch_type_systems.takeVector(); + std::sort(scratch_type_systems.begin(), scratch_type_systems.end(), + [](lldb::TypeSystemSP a, lldb::TypeSystemSP b) { + return a.get() <= b.get(); + }); + scratch_type_systems.erase( + std::unique(scratch_type_systems.begin(), scratch_type_systems.end()), + scratch_type_systems.end()); + return scratch_type_systems; } PersistentExpressionState * @@ -2351,7 +2406,13 @@ Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) { return nullptr; } - return type_system_or_err->GetPersistentExpressionState(); + if (auto ts = *type_system_or_err) + return ts->GetPersistentExpressionState(); + + LLDB_LOG(GetLog(LLDBLog::Target), + "Unable to get persistent expression state for language {}", + Language::GetNameForLanguageType(language)); + return nullptr; } UserExpression *Target::GetUserExpressionForLanguage( @@ -2368,8 +2429,16 @@ UserExpression *Target::GetUserExpressionForLanguage( return nullptr; } - auto *user_expr = type_system_or_err->GetUserExpression( - expr, prefix, language, desired_type, options, ctx_obj); + auto ts = *type_system_or_err; + if (!ts) { + error.SetErrorStringWithFormat( + "Type system for language %s is no longer live", + Language::GetNameForLanguageType(language)); + return nullptr; + } + + auto *user_expr = ts->GetUserExpression(expr, prefix, language, desired_type, + options, ctx_obj); if (!user_expr) error.SetErrorStringWithFormat( "Could not create an expression for language %s", @@ -2390,9 +2459,15 @@ FunctionCaller *Target::GetFunctionCallerForLanguage( llvm::toString(std::move(err)).c_str()); return nullptr; } - - auto *persistent_fn = type_system_or_err->GetFunctionCaller( - return_type, function_address, arg_value_list, name); + auto ts = *type_system_or_err; + if (!ts) { + error.SetErrorStringWithFormat( + "Type system for language %s is no longer live", + Language::GetNameForLanguageType(language)); + return nullptr; + } + auto *persistent_fn = ts->GetFunctionCaller(return_type, function_address, + arg_value_list, name); if (!persistent_fn) error.SetErrorStringWithFormat( "Could not create an expression for language %s", @@ -2408,10 +2483,15 @@ Target::CreateUtilityFunction(std::string expression, std::string name, auto type_system_or_err = GetScratchTypeSystemForLanguage(language); if (!type_system_or_err) return type_system_or_err.takeError(); - + auto ts = *type_system_or_err; + if (!ts) + return llvm::make_error<llvm::StringError>( + llvm::StringRef("Type system for language ") + + Language::GetNameForLanguageType(language) + + llvm::StringRef(" is no longer live"), + llvm::inconvertibleErrorCode()); std::unique_ptr<UtilityFunction> utility_fn = - type_system_or_err->CreateUtilityFunction(std::move(expression), - std::move(name)); + ts->CreateUtilityFunction(std::move(expression), std::move(name)); if (!utility_fn) return llvm::make_error<llvm::StringError>( llvm::StringRef("Could not create an expression for language") + @@ -2505,8 +2585,13 @@ ExpressionResults Target::EvaluateExpression( LLDB_LOG_ERROR(GetLog(LLDBLog::Target), std::move(err), "Unable to get scratch type system"); } else { - persistent_var_sp = - type_system_or_err->GetPersistentExpressionState()->GetVariable(expr); + auto ts = *type_system_or_err; + if (!ts) + LLDB_LOG_ERROR(GetLog(LLDBLog::Target), std::move(err), + "Scratch type system is no longer live"); + else + persistent_var_sp = + ts->GetPersistentExpressionState()->GetVariable(expr); } } if (persistent_var_sp) { @@ -2518,6 +2603,10 @@ ExpressionResults Target::EvaluateExpression( execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix, result_valobj_sp, error, fixed_expression, ctx_obj); + // Pass up the error by wrapping it inside an error result. + if (error.Fail() && !result_valobj_sp) + result_valobj_sp = ValueObjectConstResult::Create( + exe_ctx.GetBestExecutionContextScope(), error); } if (execution_results == eExpressionCompleted) @@ -2530,9 +2619,12 @@ ExpressionResults Target::EvaluateExpression( lldb::ExpressionVariableSP Target::GetPersistentVariable(ConstString name) { lldb::ExpressionVariableSP variable_sp; m_scratch_type_system_map.ForEach( - [name, &variable_sp](TypeSystem *type_system) -> bool { + [name, &variable_sp](TypeSystemSP type_system) -> bool { + auto ts = type_system.get(); + if (!ts) + return true; if (PersistentExpressionState *persistent_state = - type_system->GetPersistentExpressionState()) { + ts->GetPersistentExpressionState()) { variable_sp = persistent_state->GetVariable(name); if (variable_sp) @@ -2547,9 +2639,13 @@ lldb::addr_t Target::GetPersistentSymbol(ConstString name) { lldb::addr_t address = LLDB_INVALID_ADDRESS; m_scratch_type_system_map.ForEach( - [name, &address](TypeSystem *type_system) -> bool { + [name, &address](lldb::TypeSystemSP type_system) -> bool { + auto ts = type_system.get(); + if (!ts) + return true; + if (PersistentExpressionState *persistent_state = - type_system->GetPersistentExpressionState()) { + ts->GetPersistentExpressionState()) { address = persistent_state->LookupSymbol(name); if (address != LLDB_INVALID_ADDRESS) return false; // Stop iterating the ForEach @@ -3109,7 +3205,7 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { assert(launch_info.GetHijackListener()); EventSP first_stop_event_sp; - state = m_process_sp->WaitForProcessToStop(llvm::None, &first_stop_event_sp, + state = m_process_sp->WaitForProcessToStop(std::nullopt, &first_stop_event_sp, rebroadcast_first_stop, launch_info.GetHijackListener()); m_process_sp->RestoreProcessEvents(); @@ -3219,8 +3315,8 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { // the process to attach to by default if (!attach_info.ProcessInfoSpecified()) { if (old_exec_module_sp) - attach_info.GetExecutableFile().GetFilename() = - old_exec_module_sp->GetPlatformFileSpec().GetFilename(); + attach_info.GetExecutableFile().SetFilename( + old_exec_module_sp->GetPlatformFileSpec().GetFilename()); if (!attach_info.ProcessInfoSpecified()) { return Status("no process specified, create a target with a file, or " @@ -3265,8 +3361,9 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { if (async) { process_sp->RestoreProcessEvents(); } else { - state = process_sp->WaitForProcessToStop( - llvm::None, nullptr, false, attach_info.GetHijackListener(), stream); + state = process_sp->WaitForProcessToStop(std::nullopt, nullptr, false, + attach_info.GetHijackListener(), + stream); process_sp->RestoreProcessEvents(); if (state != eStateStopped) { @@ -3362,7 +3459,7 @@ void Target::FinalizeFileActions(ProcessLaunchInfo &info) { } } -void Target::AddDummySignal(llvm::StringRef name, LazyBool pass, LazyBool notify, +void Target::AddDummySignal(llvm::StringRef name, LazyBool pass, LazyBool notify, LazyBool stop) { if (name.empty()) return; @@ -3377,38 +3474,38 @@ void Target::AddDummySignal(llvm::StringRef name, LazyBool pass, LazyBool notify elem.stop = stop; } -bool Target::UpdateSignalFromDummy(UnixSignalsSP signals_sp, +bool Target::UpdateSignalFromDummy(UnixSignalsSP signals_sp, const DummySignalElement &elem) { if (!signals_sp) return false; - int32_t signo + int32_t signo = signals_sp->GetSignalNumberFromName(elem.first().str().c_str()); if (signo == LLDB_INVALID_SIGNAL_NUMBER) return false; - + if (elem.second.pass == eLazyBoolYes) signals_sp->SetShouldSuppress(signo, false); else if (elem.second.pass == eLazyBoolNo) signals_sp->SetShouldSuppress(signo, true); - + if (elem.second.notify == eLazyBoolYes) signals_sp->SetShouldNotify(signo, true); else if (elem.second.notify == eLazyBoolNo) signals_sp->SetShouldNotify(signo, false); - + if (elem.second.stop == eLazyBoolYes) signals_sp->SetShouldStop(signo, true); else if (elem.second.stop == eLazyBoolNo) signals_sp->SetShouldStop(signo, false); - return true; + return true; } -bool Target::ResetSignalFromDummy(UnixSignalsSP signals_sp, +bool Target::ResetSignalFromDummy(UnixSignalsSP signals_sp, const DummySignalElement &elem) { if (!signals_sp) return false; - int32_t signo + int32_t signo = signals_sp->GetSignalNumberFromName(elem.first().str().c_str()); if (signo == LLDB_INVALID_SIGNAL_NUMBER) return false; @@ -3419,14 +3516,14 @@ bool Target::ResetSignalFromDummy(UnixSignalsSP signals_sp, return true; } -void Target::UpdateSignalsFromDummy(UnixSignalsSP signals_sp, +void Target::UpdateSignalsFromDummy(UnixSignalsSP signals_sp, StreamSP warning_stream_sp) { if (!signals_sp) return; for (const auto &elem : m_dummy_signals) { if (!UpdateSignalFromDummy(signals_sp, elem)) - warning_stream_sp->Printf("Target signal '%s' not found in process\n", + warning_stream_sp->Printf("Target signal '%s' not found in process\n", elem.first().str().c_str()); } } @@ -3459,7 +3556,7 @@ void Target::ClearDummySignals(Args &signal_names) { void Target::PrintDummySignals(Stream &strm, Args &signal_args) { strm.Printf("NAME PASS STOP NOTIFY\n"); strm.Printf("=========== ======= ======= =======\n"); - + auto str_for_lazy = [] (LazyBool lazy) -> const char * { switch (lazy) { case eLazyBoolCalculate: return "not set"; @@ -4267,6 +4364,12 @@ PathMappingList &TargetProperties::GetSourcePathMap() const { return option_value->GetCurrentValue(); } +bool TargetProperties::GetAutoSourceMapRelative() const { + const uint32_t idx = ePropertyAutoSourceMapRelative; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + void TargetProperties::AppendExecutableSearchPaths(const FileSpec &dir) { const uint32_t idx = ePropertyExecutableSearchPaths; OptionValueFileSpecList *option_value = @@ -4372,7 +4475,7 @@ void TargetProperties::CheckJITObjectsDir() { else if (!writable) os << "is not writable"; - llvm::Optional<lldb::user_id_t> debugger_id = llvm::None; + std::optional<lldb::user_id_t> debugger_id; if (m_target) debugger_id = m_target->GetDebugger().GetID(); Debugger::ReportError(os.str(), debugger_id); diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp index 829036976a21..8ce2ae8c2898 100644 --- a/lldb/source/Target/TargetList.cpp +++ b/lldb/source/Target/TargetList.cpp @@ -216,7 +216,8 @@ Status TargetList::CreateTargetInternal( // If we have a valid architecture, make sure the current platform is // compatible with that architecture. if (!prefer_platform_arch && arch.IsValid()) { - if (!platform_sp->IsCompatibleArchitecture(arch, {}, false, nullptr)) { + if (!platform_sp->IsCompatibleArchitecture( + arch, {}, ArchSpec::CompatibleMatch, nullptr)) { platform_sp = platform_list.GetOrCreate(arch, {}, &platform_arch); if (platform_sp) platform_list.SetSelectedPlatform(platform_sp); @@ -225,7 +226,8 @@ Status TargetList::CreateTargetInternal( // If "arch" isn't valid, yet "platform_arch" is, it means we have an // executable file with a single architecture which should be used. ArchSpec fixed_platform_arch; - if (!platform_sp->IsCompatibleArchitecture(platform_arch, {}, false, nullptr)) { + if (!platform_sp->IsCompatibleArchitecture( + platform_arch, {}, ArchSpec::CompatibleMatch, nullptr)) { platform_sp = platform_list.GetOrCreate(platform_arch, {}, &fixed_platform_arch); if (platform_sp) @@ -256,8 +258,8 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, ArchSpec arch(specified_arch); if (arch.IsValid()) { - if (!platform_sp || - !platform_sp->IsCompatibleArchitecture(arch, {}, false, nullptr)) + if (!platform_sp || !platform_sp->IsCompatibleArchitecture( + arch, {}, ArchSpec::CompatibleMatch, nullptr)) platform_sp = debugger.GetPlatformList().GetOrCreate(specified_arch, {}, &arch); } @@ -354,7 +356,7 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, } if (file.GetDirectory()) { FileSpec file_dir; - file_dir.GetDirectory() = file.GetDirectory(); + file_dir.SetDirectory(file.GetDirectory()); target_sp->AppendExecutableSearchPaths(file_dir); } diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index acee09ca0469..202304174bc1 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -37,6 +37,9 @@ let Definition = "target" in { def SourceMap: Property<"source-map", "PathMap">, DefaultStringValue<"">, Desc<"Source path remappings apply substitutions to the paths of source files, typically needed to debug from a different host than the one that built the target. The source-map property consists of an array of pairs, the first element is a path prefix, and the second is its replacement. The syntax is `prefix1 replacement1 prefix2 replacement2...`. The pairs are checked in order, the first prefix that matches is used, and that prefix is substituted with the replacement. A common pattern is to use source-map in conjunction with the clang -fdebug-prefix-map flag. In the build, use `-fdebug-prefix-map=/path/to/build_dir=.` to rewrite the host specific build directory to `.`. Then for debugging, use `settings set target.source-map . /path/to/local_dir` to convert `.` to a valid local path.">; + def AutoSourceMapRelative: Property<"auto-source-map-relative", "Boolean">, + DefaultTrue, + Desc<"Automatically deduce source path mappings based on source file breakpoint resolution. It only deduces source mapping if source file breakpoint request is using full path and if the debug info contains relative paths.">; def ExecutableSearchPaths: Property<"exec-search-paths", "FileSpecList">, DefaultStringValue<"">, Desc<"Executable search paths to use when locating executable files whose paths don't match the local file system.">; diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index f63b57fd4e62..d620f746339e 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -53,6 +53,7 @@ #include "lldb/lldb-enumerations.h" #include <memory> +#include <optional> using namespace lldb; using namespace lldb_private; @@ -221,6 +222,7 @@ Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id) Thread::GetStaticBroadcasterClass().AsCString()), m_process_wp(process.shared_from_this()), m_stop_info_sp(), m_stop_info_stop_id(0), m_stop_info_override_stop_id(0), + m_should_run_before_public_stop(false), m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32 : process.GetNextThreadIndexID(tid)), m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(), @@ -369,7 +371,10 @@ void Thread::CalculatePublicStopInfo() { SetStopInfo(GetStopInfo()); } -lldb::StopInfoSP Thread::GetPrivateStopInfo() { +lldb::StopInfoSP Thread::GetPrivateStopInfo(bool calculate) { + if (!calculate) + return m_stop_info_sp; + if (m_destroy_called) return m_stop_info_sp; @@ -377,9 +382,15 @@ lldb::StopInfoSP Thread::GetPrivateStopInfo() { if (process_sp) { const uint32_t process_stop_id = process_sp->GetStopID(); if (m_stop_info_stop_id != process_stop_id) { + // We preserve the old stop info for a variety of reasons: + // 1) Someone has already updated it by the time we get here + // 2) We didn't get to execute the breakpoint instruction we stopped at + // 3) This is a virtual step so we didn't actually run + // 4) If this thread wasn't allowed to run the last time round. if (m_stop_info_sp) { if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() || - GetCurrentPlan()->IsVirtualStep()) + GetCurrentPlan()->IsVirtualStep() + || GetTemporaryResumeState() == eStateSuspended) SetStopInfo(m_stop_info_sp); else m_stop_info_sp.reset(); @@ -723,7 +734,11 @@ bool Thread::ShouldResume(StateType resume_state) { return need_to_resume; } -void Thread::DidResume() { SetResumeSignal(LLDB_INVALID_SIGNAL_NUMBER); } +void Thread::DidResume() { + SetResumeSignal(LLDB_INVALID_SIGNAL_NUMBER); + // This will get recomputed each time when we stop. + SetShouldRunBeforePublicStop(false); +} void Thread::DidStop() { SetState(eStateStopped); } @@ -763,6 +778,9 @@ bool Thread::ShouldStop(Event *event_ptr) { : LLDB_INVALID_ADDRESS); return false; } + + // Clear the "must run me before stop" if it was set: + SetShouldRunBeforePublicStop(false); if (log) { LLDB_LOGF(log, @@ -845,9 +863,14 @@ bool Thread::ShouldStop(Event *event_ptr) { // stack below. done_processing_current_plan = (plan_ptr->IsControllingPlan() && !plan_ptr->OkayToDiscard()); - } else + } else { + bool should_force_run = plan_ptr->ShouldRunBeforePublicStop(); + if (should_force_run) { + SetShouldRunBeforePublicStop(true); + should_stop = false; + } done_processing_current_plan = true; - + } break; } } @@ -1062,7 +1085,7 @@ ThreadPlanStack &Thread::GetPlans() const { // queries GetDescription makes, and only assert if you try to run the thread. if (!m_null_plan_stack_up) m_null_plan_stack_up = std::make_unique<ThreadPlanStack>(*this, true); - return *(m_null_plan_stack_up.get()); + return *m_null_plan_stack_up; } void Thread::PushPlan(ThreadPlanSP thread_plan_sp) { @@ -1641,6 +1664,10 @@ addr_t Thread::GetThreadLocalData(const ModuleSP module, bool Thread::SafeToCallFunctions() { Process *process = GetProcess().get(); if (process) { + DynamicLoader *loader = GetProcess()->GetDynamicLoader(); + if (loader && loader->IsFullyInitialized() == false) + return false; + SystemRuntime *runtime = process->GetSystemRuntime(); if (runtime) { return runtime->SafeToCallFunctionsOnThisThread(shared_from_this()); @@ -2020,10 +2047,10 @@ lldb::ValueObjectSP Thread::GetSiginfoValue() { if (!type.IsValid()) return ValueObjectConstResult::Create(&target, Status("no siginfo_t for the platform")); - llvm::Optional<uint64_t> type_size = type.GetByteSize(nullptr); + std::optional<uint64_t> type_size = type.GetByteSize(nullptr); assert(type_size); llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> data = - GetSiginfo(type_size.value()); + GetSiginfo(*type_size); if (!data) return ValueObjectConstResult::Create(&target, Status(data.takeError())); diff --git a/lldb/source/Target/ThreadCollection.cpp b/lldb/source/Target/ThreadCollection.cpp index 8c2309795a4c..45ce2fd318b7 100644 --- a/lldb/source/Target/ThreadCollection.cpp +++ b/lldb/source/Target/ThreadCollection.cpp @@ -34,10 +34,10 @@ void ThreadCollection::AddThreadSortedByIndexID(const ThreadSP &thread_sp) { m_threads.push_back(thread_sp); else { m_threads.insert( - std::upper_bound(m_threads.begin(), m_threads.end(), thread_sp, - [](const ThreadSP &lhs, const ThreadSP &rhs) -> bool { - return lhs->GetIndexID() < rhs->GetIndexID(); - }), + llvm::upper_bound(m_threads, thread_sp, + [](const ThreadSP &lhs, const ThreadSP &rhs) -> bool { + return lhs->GetIndexID() < rhs->GetIndexID(); + }), thread_sp); } } diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp index 7359bfcd3cfc..006c8648be52 100644 --- a/lldb/source/Target/ThreadList.cpp +++ b/lldb/source/Target/ThreadList.cpp @@ -245,14 +245,17 @@ bool ThreadList::ShouldStop(Event *event_ptr) { for (lldb::ThreadSP thread_sp : m_threads) { // This is an optimization... If we didn't let a thread run in between // the previous stop and this one, we shouldn't have to consult it for - // ShouldStop. So just leave it off the list we are going to inspect. On - // Linux, if a thread-specific conditional breakpoint was hit, it won't + // ShouldStop. So just leave it off the list we are going to inspect. + // If the thread didn't run but had work to do before declaring a public + // stop, then also include it. + // On Linux, if a thread-specific conditional breakpoint was hit, it won't // necessarily be the thread that hit the breakpoint itself that // evaluates the conditional expression, so the thread that hit the // breakpoint could still be asked to stop, even though it hasn't been // allowed to run since the previous stop. if (thread_sp->GetTemporaryResumeState() != eStateSuspended || - thread_sp->IsStillAtLastBreakpointHit()) + thread_sp->IsStillAtLastBreakpointHit() + || thread_sp->ShouldRunBeforePublicStop()) threads_copy.push_back(thread_sp); } @@ -300,6 +303,10 @@ bool ThreadList::ShouldStop(Event *event_ptr) { thread_sp->GetStopInfo(); } + // If a thread needs to finish some job that can be done just on this thread + // before broadcastion the stop, it will signal that by returning true for + // ShouldRunBeforePublicStop. This variable gathers the results from that. + bool a_thread_needs_to_run = false; for (pos = threads_copy.begin(); pos != end; ++pos) { ThreadSP thread_sp(*pos); @@ -329,11 +336,23 @@ bool ThreadList::ShouldStop(Event *event_ptr) { did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason(); const bool thread_should_stop = thread_sp->ShouldStop(event_ptr); + if (thread_should_stop) should_stop |= true; + else { + bool this_thread_forces_run = thread_sp->ShouldRunBeforePublicStop(); + a_thread_needs_to_run |= this_thread_forces_run; + if (this_thread_forces_run) + LLDB_LOG(log, + "ThreadList::{0} thread: {1:x}, " + "says it needs to run before public stop.", + __FUNCTION__, thread_sp->GetID()); + } } - if (!should_stop && !did_anybody_stop_for_a_reason) { + if (a_thread_needs_to_run) { + should_stop = false; + } else if (!should_stop && !did_anybody_stop_for_a_reason) { should_stop = true; LLDB_LOGF(log, "ThreadList::%s we stopped but no threads had a stop reason, " @@ -368,9 +387,17 @@ Vote ThreadList::ShouldReportStop(Event *event_ptr) { // Run through the threads and ask whether we should report this event. For // stopping, a YES vote wins over everything. A NO vote wins over NO - // opinion. + // opinion. The exception is if a thread has work it needs to force before + // a public stop, which overrides everyone else's opinion: for (pos = m_threads.begin(); pos != end; ++pos) { ThreadSP thread_sp(*pos); + if (thread_sp->ShouldRunBeforePublicStop()) { + LLDB_LOG(log, "Thread {0:x} has private business to complete, overrode " + "the should report stop.", thread_sp->GetID()); + result = eVoteNo; + break; + } + const Vote vote = thread_sp->ShouldReportStop(event_ptr); switch (vote) { case eVoteNoOpinion: @@ -550,7 +577,13 @@ bool ThreadList::WillResume() { run_me_only_list.SetStopID(m_process->GetStopID()); - bool run_only_current_thread = false; + // One or more threads might want to "Stop Others". We want to handle all + // those requests first. And if there is a thread that wanted to "resume + // before a public stop", let it get the first crack: + // There are two special kinds of thread that have priority for "StopOthers": + // a "ShouldRunBeforePublicStop thread, or the currently selected thread. If + // we find one satisfying that critereon, put it here. + ThreadSP stop_others_thread_sp; for (pos = m_threads.begin(); pos != end; ++pos) { ThreadSP thread_sp(*pos); @@ -562,17 +595,16 @@ bool ThreadList::WillResume() { // You can't say "stop others" and also want yourself to be suspended. assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); + run_me_only_list.AddThread(thread_sp); - if (thread_sp == GetSelectedThread()) { - // If the currently selected thread wants to run on its own, always let - // it. - run_only_current_thread = true; - run_me_only_list.Clear(); - run_me_only_list.AddThread(thread_sp); + if (thread_sp == GetSelectedThread()) + stop_others_thread_sp = thread_sp; + + if (thread_sp->ShouldRunBeforePublicStop()) { + // This takes precedence, so if we find one of these, service it: + stop_others_thread_sp = thread_sp; break; } - - run_me_only_list.AddThread(thread_sp); } } @@ -593,8 +625,8 @@ bool ThreadList::WillResume() { } else { ThreadSP thread_to_run; - if (run_only_current_thread) { - thread_to_run = GetSelectedThread(); + if (stop_others_thread_sp) { + thread_to_run = stop_others_thread_sp; } else if (run_me_only_list.GetSize(false) == 1) { thread_to_run = run_me_only_list.GetThreadAtIndex(0); } else { @@ -607,6 +639,9 @@ bool ThreadList::WillResume() { for (pos = m_threads.begin(); pos != end; ++pos) { ThreadSP thread_sp(*pos); if (thread_sp == thread_to_run) { + // Note, a thread might be able to fulfil it's plan w/o actually + // resuming. An example of this is a step that changes the current + // inlined function depth w/o moving the PC. Check that here: if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState())) need_to_resume = false; } else @@ -624,7 +659,7 @@ void ThreadList::DidResume() { // Don't clear out threads that aren't going to get a chance to run, rather // leave their state for the next time around. ThreadSP thread_sp(*pos); - if (thread_sp->GetResumeState() != eStateSuspended) + if (thread_sp->GetTemporaryResumeState() != eStateSuspended) thread_sp->DidResume(); } } diff --git a/lldb/source/Target/ThreadPlanStack.cpp b/lldb/source/Target/ThreadPlanStack.cpp index ac7b44cef37d..157293142907 100644 --- a/lldb/source/Target/ThreadPlanStack.cpp +++ b/lldb/source/Target/ThreadPlanStack.cpp @@ -406,7 +406,7 @@ void ThreadPlanStackMap::Update(ThreadList ¤t_threads, for (auto thread : current_threads.Threads()) { lldb::tid_t cur_tid = thread->GetID(); if (!Find(cur_tid)) { - AddThread(*thread.get()); + AddThread(*thread); thread->QueueBasePlan(true); } } diff --git a/lldb/source/Target/ThreadPlanTracer.cpp b/lldb/source/Target/ThreadPlanTracer.cpp index 7e0925307644..82927ef64877 100644 --- a/lldb/source/Target/ThreadPlanTracer.cpp +++ b/lldb/source/Target/ThreadPlanTracer.cpp @@ -110,10 +110,10 @@ TypeFromUser ThreadPlanAssemblyTracer::GetIntPointerType() { LLDB_LOG_ERROR(GetLog(LLDBLog::Types), std::move(err), "Unable to get integer pointer type from TypeSystem"); } else { - m_intptr_type = TypeFromUser( - type_system_or_err->GetBuiltinTypeForEncodingAndBitSize( - eEncodingUint, - target_sp->GetArchitecture().GetAddressByteSize() * 8)); + if (auto ts = *type_system_or_err) + m_intptr_type = TypeFromUser(ts->GetBuiltinTypeForEncodingAndBitSize( + eEncodingUint, + target_sp->GetArchitecture().GetAddressByteSize() * 8)); } } } diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp index ac832734b8d0..1ffd617a80fc 100644 --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -19,6 +19,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Stream.h" +#include <optional> using namespace lldb; using namespace lldb_private; @@ -49,10 +50,10 @@ bool fromJSON(const Value &value, JSONSimpleTraceBundleDescription &bundle, /// limitations in move constructors. /// \{ template <typename K, typename V> -static Optional<V> Lookup(DenseMap<K, V> &map, K k) { +static std::optional<V> Lookup(DenseMap<K, V> &map, K k) { auto it = map.find(k); if (it == map.end()) - return None; + return std::nullopt; return it->second; } @@ -66,10 +67,11 @@ static V *LookupAsPtr(DenseMap<K, V> &map, K k) { /// Similar to the methods above but it looks for an item in a map of maps. template <typename K1, typename K2, typename V> -static Optional<V> Lookup(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, K2 k2) { +static std::optional<V> Lookup(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, + K2 k2) { auto it = map.find(k1); if (it == map.end()) - return None; + return std::nullopt; return Lookup(it->second, k2); } @@ -182,19 +184,20 @@ Expected<std::string> Trace::GetLiveProcessState() { return m_live_process->TraceGetState(GetPluginName()); } -Optional<uint64_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, - llvm::StringRef kind) { +std::optional<uint64_t> +Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, llvm::StringRef kind) { Storage &storage = GetUpdatedStorage(); return Lookup(storage.live_thread_data, tid, ConstString(kind)); } -Optional<uint64_t> Trace::GetLiveCpuBinaryDataSize(lldb::cpu_id_t cpu_id, - llvm::StringRef kind) { +std::optional<uint64_t> Trace::GetLiveCpuBinaryDataSize(lldb::cpu_id_t cpu_id, + llvm::StringRef kind) { Storage &storage = GetUpdatedStorage(); return Lookup(storage.live_cpu_data_sizes, cpu_id, ConstString(kind)); } -Optional<uint64_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { +std::optional<uint64_t> +Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { Storage &storage = GetUpdatedStorage(); return Lookup(storage.live_process_data, ConstString(kind)); } @@ -228,7 +231,7 @@ Trace::GetLiveTraceBinaryData(const TraceGetBinaryDataRequest &request, Expected<std::vector<uint8_t>> Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { - llvm::Optional<uint64_t> size = GetLiveThreadBinaryDataSize(tid, kind); + std::optional<uint64_t> size = GetLiveThreadBinaryDataSize(tid, kind); if (!size) return createStringError( inconvertibleErrorCode(), @@ -236,7 +239,7 @@ Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { kind.data(), tid); TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, - /*cpu_id=*/None}; + /*cpu_id=*/std::nullopt}; return GetLiveTraceBinaryData(request, *size); } @@ -246,7 +249,7 @@ Trace::GetLiveCpuBinaryData(lldb::cpu_id_t cpu_id, llvm::StringRef kind) { return createStringError( inconvertibleErrorCode(), "Attempted to fetch live cpu data without a live process."); - llvm::Optional<uint64_t> size = GetLiveCpuBinaryDataSize(cpu_id, kind); + std::optional<uint64_t> size = GetLiveCpuBinaryDataSize(cpu_id, kind); if (!size) return createStringError( inconvertibleErrorCode(), @@ -254,20 +257,21 @@ Trace::GetLiveCpuBinaryData(lldb::cpu_id_t cpu_id, llvm::StringRef kind) { kind.data(), cpu_id); TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), - /*tid=*/None, cpu_id}; + /*tid=*/std::nullopt, cpu_id}; return m_live_process->TraceGetBinaryData(request); } Expected<std::vector<uint8_t>> Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { - llvm::Optional<uint64_t> size = GetLiveProcessBinaryDataSize(kind); + std::optional<uint64_t> size = GetLiveProcessBinaryDataSize(kind); if (!size) return createStringError( inconvertibleErrorCode(), "Tracing data \"%s\" is not available for the process.", kind.data()); TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), - /*tid=*/None, /*cpu_id*/ None}; + /*tid=*/std::nullopt, + /*cpu_id*/ std::nullopt}; return GetLiveTraceBinaryData(request, *size); } @@ -344,7 +348,7 @@ const char *Trace::RefreshLiveProcessState() { } Trace::Trace(ArrayRef<ProcessSP> postmortem_processes, - Optional<std::vector<lldb::cpu_id_t>> postmortem_cpus) { + std::optional<std::vector<lldb::cpu_id_t>> postmortem_cpus) { for (ProcessSP process_sp : postmortem_processes) m_storage.postmortem_processes.push_back(process_sp.get()); m_storage.cpus = postmortem_cpus; @@ -370,7 +374,7 @@ uint32_t Trace::GetStopID() { llvm::Expected<FileSpec> Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { Storage &storage = GetUpdatedStorage(); - if (Optional<FileSpec> file = + if (std::optional<FileSpec> file = Lookup(storage.postmortem_thread_data, tid, ConstString(kind))) return *file; else @@ -383,7 +387,7 @@ Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { llvm::Expected<FileSpec> Trace::GetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id, llvm::StringRef kind) { Storage &storage = GetUpdatedStorage(); - if (Optional<FileSpec> file = + if (std::optional<FileSpec> file = Lookup(storage.postmortem_cpu_data, cpu_id, ConstString(kind))) return *file; else @@ -437,7 +441,7 @@ llvm::Error Trace::OnDataFileRead(FileSpec file, if (std::error_code err = trace_or_error.getError()) return createStringError( inconvertibleErrorCode(), "Failed fetching trace-related file %s. %s", - file.GetCString(), toString(errorCodeToError(err)).c_str()); + file.GetPath().c_str(), toString(errorCodeToError(err)).c_str()); MemoryBuffer &data = **trace_or_error; ArrayRef<uint8_t> array_ref( diff --git a/lldb/source/Target/TraceCursor.cpp b/lldb/source/Target/TraceCursor.cpp index de3f9bf1b33d..b85cc750a8a4 100644 --- a/lldb/source/Target/TraceCursor.cpp +++ b/lldb/source/Target/TraceCursor.cpp @@ -52,6 +52,8 @@ const char *TraceCursor::EventKindToString(lldb::TraceEvent event_kind) { return "CPU core changed"; case lldb::eTraceEventHWClockTick: return "HW clock tick"; + case lldb::eTraceEventSyncPoint: + return "trace synchronization point"; } llvm_unreachable("Fully covered switch above"); } diff --git a/lldb/source/Target/TraceDumper.cpp b/lldb/source/Target/TraceDumper.cpp index 5b71e9e4e97a..d059d443805c 100644 --- a/lldb/source/Target/TraceDumper.cpp +++ b/lldb/source/Target/TraceDumper.cpp @@ -7,34 +7,39 @@ //===----------------------------------------------------------------------===// #include "lldb/Target/TraceDumper.h" - #include "lldb/Core/Module.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" +#include <optional> using namespace lldb; using namespace lldb_private; using namespace llvm; /// \return -/// The given string or \b None if it's empty. -static Optional<const char *> ToOptionalString(const char *s) { +/// The given string or \b std::nullopt if it's empty. +static std::optional<const char *> ToOptionalString(const char *s) { if (!s) - return None; + return std::nullopt; return s; } + +static const char *GetModuleName(const SymbolContext &sc) { + if (!sc.module_sp) + return nullptr; + return sc.module_sp->GetFileSpec().GetFilename().AsCString(); +} + /// \return /// The module name (basename if the module is a file, or the actual name if /// it's a virtual module), or \b nullptr if no name nor module was found. static const char *GetModuleName(const TraceDumper::TraceItem &item) { - if (!item.symbol_info || !item.symbol_info->sc.module_sp) + if (!item.symbol_info) return nullptr; - return item.symbol_info->sc.module_sp->GetFileSpec() - .GetFilename() - .AsCString(); + return GetModuleName(item.symbol_info->sc); } // This custom LineEntry validator is neded because some line_entries have @@ -64,10 +69,12 @@ static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) { /// - module /// - symbol /// - function -/// - line +/// - inlined function +/// - source line info static bool IsSameInstructionSymbolContext(const TraceDumper::SymbolInfo &prev_insn, - const TraceDumper::SymbolInfo &insn) { + const TraceDumper::SymbolInfo &insn, + bool check_source_line_info = true) { // module checks if (insn.sc.module_sp != prev_insn.sc.module_sp) return false; @@ -78,11 +85,23 @@ IsSameInstructionSymbolContext(const TraceDumper::SymbolInfo &prev_insn, // function checks if (!insn.sc.function && !prev_insn.sc.function) - return true; + return true; // This means two dangling instruction in the same module. We + // can assume they are part of the same unnamed symbol else if (insn.sc.function != prev_insn.sc.function) return false; + Block *inline_block_a = + insn.sc.block ? insn.sc.block->GetContainingInlinedBlock() : nullptr; + Block *inline_block_b = prev_insn.sc.block + ? prev_insn.sc.block->GetContainingInlinedBlock() + : nullptr; + if (inline_block_a != inline_block_b) + return false; + // line entry checks + if (!check_source_line_info) + return true; + const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry); const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry); if (curr_line_valid && prev_line_valid) @@ -100,6 +119,14 @@ public: void NoMoreData() override { m_s << " no more data\n"; } + void FunctionCallForest( + const std::vector<TraceDumper::FunctionCallUP> &forest) override { + for (size_t i = 0; i < forest.size(); i++) { + m_s.Format("\n[call tree #{0}]\n", i); + DumpFunctionCallTree(*forest[i]); + } + } + void TraceItem(const TraceDumper::TraceItem &item) override { if (item.symbol_info) { if (!item.prev_symbol_info || @@ -148,6 +175,9 @@ public: case eTraceEventDisabledHW: case eTraceEventDisabledSW: break; + case eTraceEventSyncPoint: + m_s.Format(" [{0}]", item.sync_point_metadata); + break; } } else if (item.error) { m_s << "(error) " << *item.error; @@ -171,6 +201,78 @@ public: } private: + void + DumpSegmentContext(const TraceDumper::FunctionCall::TracedSegment &segment) { + if (segment.GetOwningCall().IsError()) { + m_s << "<tracing errors>"; + return; + } + + const SymbolContext &first_sc = segment.GetFirstInstructionSymbolInfo().sc; + first_sc.DumpStopContext( + &m_s, segment.GetFirstInstructionSymbolInfo().exe_ctx.GetTargetPtr(), + segment.GetFirstInstructionSymbolInfo().address, + /*show_fullpaths=*/false, + /*show_module=*/true, /*show_inlined_frames=*/false, + /*show_function_arguments=*/true, + /*show_function_name=*/true); + m_s << " to "; + const SymbolContext &last_sc = segment.GetLastInstructionSymbolInfo().sc; + if (IsLineEntryValid(first_sc.line_entry) && + IsLineEntryValid(last_sc.line_entry)) { + m_s.Format("{0}:{1}", last_sc.line_entry.line, last_sc.line_entry.column); + } else { + last_sc.DumpStopContext( + &m_s, segment.GetFirstInstructionSymbolInfo().exe_ctx.GetTargetPtr(), + segment.GetLastInstructionSymbolInfo().address, + /*show_fullpaths=*/false, + /*show_module=*/false, /*show_inlined_frames=*/false, + /*show_function_arguments=*/false, + /*show_function_name=*/false); + } + } + + void DumpUntracedContext(const TraceDumper::FunctionCall &function_call) { + if (function_call.IsError()) { + m_s << "tracing error"; + } + const SymbolContext &sc = function_call.GetSymbolInfo().sc; + + const char *module_name = GetModuleName(sc); + if (!module_name) + m_s << "(none)"; + else if (!sc.function && !sc.symbol) + m_s << module_name << "`(none)"; + else + m_s << module_name << "`" << sc.GetFunctionName().AsCString(); + } + + void DumpFunctionCallTree(const TraceDumper::FunctionCall &function_call) { + if (function_call.GetUntracedPrefixSegment()) { + m_s.Indent(); + DumpUntracedContext(function_call); + m_s << "\n"; + + m_s.IndentMore(); + DumpFunctionCallTree(function_call.GetUntracedPrefixSegment()->GetNestedCall()); + m_s.IndentLess(); + } + + for (const TraceDumper::FunctionCall::TracedSegment &segment : + function_call.GetTracedSegments()) { + m_s.Indent(); + DumpSegmentContext(segment); + m_s.Format(" [{0}, {1}]\n", segment.GetFirstInstructionID(), + segment.GetLastInstructionID()); + + segment.IfNestedCall([&](const TraceDumper::FunctionCall &nested_call) { + m_s.IndentMore(); + DumpFunctionCallTree(nested_call); + m_s.IndentLess(); + }); + } + } + Stream &m_s; TraceDumperOptions m_options; bool m_was_prev_instruction_an_error = false; @@ -192,6 +294,7 @@ class OutputWriterJSON : public TraceDumper::OutputWriter { "loadAddress": string decimal, "id": decimal, "hwClock"?: string decimal, + "syncPointMetadata"?: string, "timestamp_ns"?: string decimal, "module"?: string, "symbol"?: string, @@ -199,6 +302,7 @@ class OutputWriterJSON : public TraceDumper::OutputWriter { "column"?: decimal, "source"?: string, "mnemonic"?: string, + "controlFlowKind"?: string, } */ public: @@ -211,6 +315,43 @@ public: ~OutputWriterJSON() { m_j.arrayEnd(); } + void FunctionCallForest( + const std::vector<TraceDumper::FunctionCallUP> &forest) override { + for (size_t i = 0; i < forest.size(); i++) { + m_j.object([&] { DumpFunctionCallTree(*forest[i]); }); + } + } + + void DumpFunctionCallTree(const TraceDumper::FunctionCall &function_call) { + if (function_call.GetUntracedPrefixSegment()) { + m_j.attributeObject("untracedPrefixSegment", [&] { + m_j.attributeObject("nestedCall", [&] { + DumpFunctionCallTree( + function_call.GetUntracedPrefixSegment()->GetNestedCall()); + }); + }); + } + + if (!function_call.GetTracedSegments().empty()) { + m_j.attributeArray("tracedSegments", [&] { + for (const TraceDumper::FunctionCall::TracedSegment &segment : + function_call.GetTracedSegments()) { + m_j.object([&] { + m_j.attribute("firstInstructionId", + std::to_string(segment.GetFirstInstructionID())); + m_j.attribute("lastInstructionId", + std::to_string(segment.GetLastInstructionID())); + segment.IfNestedCall( + [&](const TraceDumper::FunctionCall &nested_call) { + m_j.attributeObject( + "nestedCall", [&] { DumpFunctionCallTree(nested_call); }); + }); + }); + } + }); + } + } + void DumpEvent(const TraceDumper::TraceItem &item) { m_j.attribute("event", TraceCursor::EventKindToString(*item.event)); switch (*item.event) { @@ -223,6 +364,9 @@ public: case eTraceEventDisabledHW: case eTraceEventDisabledSW: break; + case eTraceEventSyncPoint: + m_j.attribute("syncPointMetadata", item.sync_point_metadata); + break; } } @@ -234,10 +378,18 @@ public: "symbol", ToOptionalString(item.symbol_info->sc.GetFunctionName().AsCString())); - if (item.symbol_info->instruction) { + if (lldb::InstructionSP instruction = item.symbol_info->instruction) { + ExecutionContext exe_ctx = item.symbol_info->exe_ctx; m_j.attribute("mnemonic", - ToOptionalString(item.symbol_info->instruction->GetMnemonic( - &item.symbol_info->exe_ctx))); + ToOptionalString(instruction->GetMnemonic(&exe_ctx))); + if (m_options.show_control_flow_kind) { + lldb::InstructionControlFlowKind instruction_control_flow_kind = + instruction->GetControlFlowKind(&exe_ctx); + m_j.attribute("controlFlowKind", + ToOptionalString( + Instruction::GetNameForInstructionControlFlowKind( + instruction_control_flow_kind))); + } } if (IsLineEntryValid(item.symbol_info->sc.line_entry)) { @@ -256,9 +408,9 @@ public: m_j.attribute("id", item.id); if (m_options.show_timestamps) m_j.attribute("timestamp_ns", item.timestamp - ? Optional<std::string>( + ? std::optional<std::string>( std::to_string(*item.timestamp)) - : None); + : std::nullopt); if (item.event) { DumpEvent(item); @@ -286,32 +438,32 @@ CreateWriter(Stream &s, const TraceDumperOptions &options, Thread &thread) { new OutputWriterCLI(s, options, thread)); } -TraceDumper::TraceDumper(lldb::TraceCursorUP &&cursor_up, Stream &s, +TraceDumper::TraceDumper(lldb::TraceCursorSP cursor_sp, Stream &s, const TraceDumperOptions &options) - : m_cursor_up(std::move(cursor_up)), m_options(options), + : m_cursor_sp(std::move(cursor_sp)), m_options(options), m_writer_up(CreateWriter( - s, m_options, *m_cursor_up->GetExecutionContextRef().GetThreadSP())) { + s, m_options, *m_cursor_sp->GetExecutionContextRef().GetThreadSP())) { if (m_options.id) - m_cursor_up->GoToId(*m_options.id); + m_cursor_sp->GoToId(*m_options.id); else if (m_options.forwards) - m_cursor_up->Seek(0, TraceCursor::SeekType::Beginning); + m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeBeginning); else - m_cursor_up->Seek(0, TraceCursor::SeekType::End); + m_cursor_sp->Seek(0, lldb::eTraceCursorSeekTypeEnd); - m_cursor_up->SetForwards(m_options.forwards); + m_cursor_sp->SetForwards(m_options.forwards); if (m_options.skip) { - m_cursor_up->Seek((m_options.forwards ? 1 : -1) * *m_options.skip, - TraceCursor::SeekType::Current); + m_cursor_sp->Seek((m_options.forwards ? 1 : -1) * *m_options.skip, + lldb::eTraceCursorSeekTypeCurrent); } } TraceDumper::TraceItem TraceDumper::CreatRawTraceItem() { TraceItem item = {}; - item.id = m_cursor_up->GetId(); + item.id = m_cursor_sp->GetId(); if (m_options.show_timestamps) - item.timestamp = m_cursor_up->GetWallClockTime(); + item.timestamp = m_cursor_sp->GetWallClockTime(); return item; } @@ -319,13 +471,12 @@ TraceDumper::TraceItem TraceDumper::CreatRawTraceItem() { /// instruction's symbol context when possible. static SymbolContext CalculateSymbolContext(const Address &address, - const TraceDumper::SymbolInfo &prev_symbol_info) { + const SymbolContext &prev_symbol_context) { AddressRange range; - if (prev_symbol_info.sc.GetAddressRange(eSymbolContextEverything, 0, - /*inline_block_range*/ false, - range) && + if (prev_symbol_context.GetAddressRange(eSymbolContextEverything, 0, + /*inline_block_range*/ true, range) && range.Contains(address)) - return prev_symbol_info.sc; + return prev_symbol_context; SymbolContext sc; address.CalculateSymbolContext(&sc, eSymbolContextEverything); @@ -368,59 +519,395 @@ CalculateDisass(const TraceDumper::SymbolInfo &symbol_info, : InstructionSP()); } -Optional<lldb::user_id_t> TraceDumper::DumpInstructions(size_t count) { - ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP(); +static TraceDumper::SymbolInfo +CalculateSymbolInfo(const ExecutionContext &exe_ctx, lldb::addr_t load_address, + const TraceDumper::SymbolInfo &prev_symbol_info) { + TraceDumper::SymbolInfo symbol_info; + symbol_info.exe_ctx = exe_ctx; + symbol_info.address.SetLoadAddress(load_address, exe_ctx.GetTargetPtr()); + symbol_info.sc = + CalculateSymbolContext(symbol_info.address, prev_symbol_info.sc); + std::tie(symbol_info.disassembler, symbol_info.instruction) = + CalculateDisass(symbol_info, prev_symbol_info, exe_ctx); + return symbol_info; +} + +std::optional<lldb::user_id_t> TraceDumper::DumpInstructions(size_t count) { + ThreadSP thread_sp = m_cursor_sp->GetExecutionContextRef().GetThreadSP(); SymbolInfo prev_symbol_info; - Optional<lldb::user_id_t> last_id; + std::optional<lldb::user_id_t> last_id; ExecutionContext exe_ctx; thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx); - for (size_t insn_seen = 0; insn_seen < count && m_cursor_up->HasValue(); - m_cursor_up->Next()) { + for (size_t insn_seen = 0; insn_seen < count && m_cursor_sp->HasValue(); + m_cursor_sp->Next()) { - last_id = m_cursor_up->GetId(); + last_id = m_cursor_sp->GetId(); TraceItem item = CreatRawTraceItem(); - if (m_cursor_up->IsEvent()) { - if (!m_options.show_events) - continue; - item.event = m_cursor_up->GetEventType(); + if (m_cursor_sp->IsEvent() && m_options.show_events) { + item.event = m_cursor_sp->GetEventType(); switch (*item.event) { case eTraceEventCPUChanged: - item.cpu_id = m_cursor_up->GetCPU(); + item.cpu_id = m_cursor_sp->GetCPU(); break; case eTraceEventHWClockTick: - item.hw_clock = m_cursor_up->GetHWClock(); + item.hw_clock = m_cursor_sp->GetHWClock(); break; case eTraceEventDisabledHW: case eTraceEventDisabledSW: break; + case eTraceEventSyncPoint: + item.sync_point_metadata = m_cursor_sp->GetSyncPointMetadata(); + break; } - } else if (m_cursor_up->IsError()) { - item.error = m_cursor_up->GetError(); - } else { + m_writer_up->TraceItem(item); + } else if (m_cursor_sp->IsError()) { + item.error = m_cursor_sp->GetError(); + m_writer_up->TraceItem(item); + } else if (m_cursor_sp->IsInstruction() && !m_options.only_events) { insn_seen++; - item.load_address = m_cursor_up->GetLoadAddress(); + item.load_address = m_cursor_sp->GetLoadAddress(); if (!m_options.raw) { - SymbolInfo symbol_info; - symbol_info.exe_ctx = exe_ctx; - symbol_info.address.SetLoadAddress(item.load_address, - exe_ctx.GetTargetPtr()); - symbol_info.sc = - CalculateSymbolContext(symbol_info.address, prev_symbol_info); - std::tie(symbol_info.disassembler, symbol_info.instruction) = - CalculateDisass(symbol_info, prev_symbol_info, exe_ctx); + SymbolInfo symbol_info = + CalculateSymbolInfo(exe_ctx, item.load_address, prev_symbol_info); item.prev_symbol_info = prev_symbol_info; item.symbol_info = symbol_info; prev_symbol_info = symbol_info; } + m_writer_up->TraceItem(item); } - m_writer_up->TraceItem(item); } - if (!m_cursor_up->HasValue()) + if (!m_cursor_sp->HasValue()) m_writer_up->NoMoreData(); return last_id; } + +void TraceDumper::FunctionCall::TracedSegment::AppendInsn( + const TraceCursorSP &cursor_sp, + const TraceDumper::SymbolInfo &symbol_info) { + m_last_insn_id = cursor_sp->GetId(); + m_last_symbol_info = symbol_info; +} + +lldb::user_id_t +TraceDumper::FunctionCall::TracedSegment::GetFirstInstructionID() const { + return m_first_insn_id; +} + +lldb::user_id_t +TraceDumper::FunctionCall::TracedSegment::GetLastInstructionID() const { + return m_last_insn_id; +} + +void TraceDumper::FunctionCall::TracedSegment::IfNestedCall( + std::function<void(const FunctionCall &function_call)> callback) const { + if (m_nested_call) + callback(*m_nested_call); +} + +const TraceDumper::FunctionCall & +TraceDumper::FunctionCall::TracedSegment::GetOwningCall() const { + return m_owning_call; +} + +TraceDumper::FunctionCall & +TraceDumper::FunctionCall::TracedSegment::CreateNestedCall( + const TraceCursorSP &cursor_sp, + const TraceDumper::SymbolInfo &symbol_info) { + m_nested_call = std::make_unique<FunctionCall>(cursor_sp, symbol_info); + m_nested_call->SetParentCall(m_owning_call); + return *m_nested_call; +} + +const TraceDumper::SymbolInfo & +TraceDumper::FunctionCall::TracedSegment::GetFirstInstructionSymbolInfo() + const { + return m_first_symbol_info; +} + +const TraceDumper::SymbolInfo & +TraceDumper::FunctionCall::TracedSegment::GetLastInstructionSymbolInfo() const { + return m_last_symbol_info; +} + +const TraceDumper::FunctionCall & +TraceDumper::FunctionCall::UntracedPrefixSegment::GetNestedCall() const { + return *m_nested_call; +} + +TraceDumper::FunctionCall::FunctionCall( + const TraceCursorSP &cursor_sp, + const TraceDumper::SymbolInfo &symbol_info) { + m_is_error = cursor_sp->IsError(); + AppendSegment(cursor_sp, symbol_info); +} + +void TraceDumper::FunctionCall::AppendSegment( + const TraceCursorSP &cursor_sp, + const TraceDumper::SymbolInfo &symbol_info) { + m_traced_segments.emplace_back(cursor_sp, symbol_info, *this); +} + +const TraceDumper::SymbolInfo & +TraceDumper::FunctionCall::GetSymbolInfo() const { + return m_traced_segments.back().GetLastInstructionSymbolInfo(); +} + +bool TraceDumper::FunctionCall::IsError() const { return m_is_error; } + +const std::deque<TraceDumper::FunctionCall::TracedSegment> & +TraceDumper::FunctionCall::GetTracedSegments() const { + return m_traced_segments; +} + +TraceDumper::FunctionCall::TracedSegment & +TraceDumper::FunctionCall::GetLastTracedSegment() { + return m_traced_segments.back(); +} + +const std::optional<TraceDumper::FunctionCall::UntracedPrefixSegment> & +TraceDumper::FunctionCall::GetUntracedPrefixSegment() const { + return m_untraced_prefix_segment; +} + +void TraceDumper::FunctionCall::SetUntracedPrefixSegment( + TraceDumper::FunctionCallUP &&nested_call) { + m_untraced_prefix_segment.emplace(std::move(nested_call)); +} + +TraceDumper::FunctionCall *TraceDumper::FunctionCall::GetParentCall() const { + return m_parent_call; +} + +void TraceDumper::FunctionCall::SetParentCall( + TraceDumper::FunctionCall &parent_call) { + m_parent_call = &parent_call; +} + +/// Given an instruction that happens after a return, find the ancestor function +/// call that owns it. If this ancestor doesn't exist, create a new ancestor and +/// make it the root of the tree. +/// +/// \param[in] last_function_call +/// The function call that performs the return. +/// +/// \param[in] symbol_info +/// The symbol information of the instruction after the return. +/// +/// \param[in] cursor_sp +/// The cursor pointing to the instruction after the return. +/// +/// \param[in,out] roots +/// The object owning the roots. It might be modified if a new root needs to +/// be created. +/// +/// \return +/// A reference to the function call that owns the new instruction +static TraceDumper::FunctionCall &AppendReturnedInstructionToFunctionCallForest( + TraceDumper::FunctionCall &last_function_call, + const TraceDumper::SymbolInfo &symbol_info, const TraceCursorSP &cursor_sp, + std::vector<TraceDumper::FunctionCallUP> &roots) { + + // We omit the current node because we can't return to itself. + TraceDumper::FunctionCall *ancestor = last_function_call.GetParentCall(); + + for (; ancestor; ancestor = ancestor->GetParentCall()) { + // This loop traverses the tree until it finds a call that we can return to. + if (IsSameInstructionSymbolContext(ancestor->GetSymbolInfo(), symbol_info, + /*check_source_line_info=*/false)) { + // We returned to this symbol, so we are assuming we are returning there + // Note: If this is not robust enough, we should actually check if we + // returning to the instruction that follows the last instruction from + // that call, as that's the behavior of CALL instructions. + ancestor->AppendSegment(cursor_sp, symbol_info); + return *ancestor; + } + } + + // We didn't find the call we were looking for, so we now create a synthetic + // one that will contain the new instruction in its first traced segment. + TraceDumper::FunctionCallUP new_root = + std::make_unique<TraceDumper::FunctionCall>(cursor_sp, symbol_info); + // This new root will own the previous root through an untraced prefix segment. + new_root->SetUntracedPrefixSegment(std::move(roots.back())); + roots.pop_back(); + // We update the roots container to point to the new root + roots.emplace_back(std::move(new_root)); + return *roots.back(); +} + +/// Append an instruction to a function call forest. The new instruction might +/// be appended to the current segment, to a new nest call, or return to an +/// ancestor call. +/// +/// \param[in] exe_ctx +/// The exeuction context of the traced thread. +/// +/// \param[in] last_function_call +/// The chronologically most recent function call before the new instruction. +/// +/// \param[in] prev_symbol_info +/// The symbol information of the previous instruction in the trace. +/// +/// \param[in] symbol_info +/// The symbol information of the new instruction. +/// +/// \param[in] cursor_sp +/// The cursor pointing to the new instruction. +/// +/// \param[in,out] roots +/// The object owning the roots. It might be modified if a new root needs to +/// be created. +/// +/// \return +/// A reference to the function call that owns the new instruction. +static TraceDumper::FunctionCall &AppendInstructionToFunctionCallForest( + const ExecutionContext &exe_ctx, + TraceDumper::FunctionCall *last_function_call, + const TraceDumper::SymbolInfo &prev_symbol_info, + const TraceDumper::SymbolInfo &symbol_info, const TraceCursorSP &cursor_sp, + std::vector<TraceDumper::FunctionCallUP> &roots) { + if (!last_function_call || last_function_call->IsError()) { + // We create a brand new root + roots.emplace_back( + std::make_unique<TraceDumper::FunctionCall>(cursor_sp, symbol_info)); + return *roots.back(); + } + + AddressRange range; + if (symbol_info.sc.GetAddressRange( + eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol, + 0, /*inline_block_range*/ true, range)) { + if (range.GetBaseAddress() == symbol_info.address) { + // Our instruction is the first instruction of a function. This has + // to be a call. This should also identify if a trampoline or the linker + // is making a call using a non-CALL instruction. + return last_function_call->GetLastTracedSegment().CreateNestedCall( + cursor_sp, symbol_info); + } + } + if (IsSameInstructionSymbolContext(prev_symbol_info, symbol_info, + /*check_source_line_info=*/false)) { + // We are still in the same function. This can't be a call because otherwise + // we would be in the first instruction of the symbol. + last_function_call->GetLastTracedSegment().AppendInsn(cursor_sp, + symbol_info); + return *last_function_call; + } + // Now we are in a different symbol. Let's see if this is a return or a + // call + const InstructionSP &insn = last_function_call->GetLastTracedSegment() + .GetLastInstructionSymbolInfo() + .instruction; + InstructionControlFlowKind insn_kind = + insn ? insn->GetControlFlowKind(&exe_ctx) + : eInstructionControlFlowKindOther; + + switch (insn_kind) { + case lldb::eInstructionControlFlowKindCall: + case lldb::eInstructionControlFlowKindFarCall: { + // This is a regular call + return last_function_call->GetLastTracedSegment().CreateNestedCall( + cursor_sp, symbol_info); + } + case lldb::eInstructionControlFlowKindFarReturn: + case lldb::eInstructionControlFlowKindReturn: { + // We should have caught most trampolines and linker functions earlier, so + // let's assume this is a regular return. + return AppendReturnedInstructionToFunctionCallForest( + *last_function_call, symbol_info, cursor_sp, roots); + } + default: + // we changed symbols not using a call or return and we are not in the + // beginning of a symbol, so this should be something very artificial + // or maybe a jump to some label in the middle of it section. + + // We first check if it's a return from an inline method + if (prev_symbol_info.sc.block && + prev_symbol_info.sc.block->GetContainingInlinedBlock()) { + return AppendReturnedInstructionToFunctionCallForest( + *last_function_call, symbol_info, cursor_sp, roots); + } + // Now We assume it's a call. We should revisit this in the future. + // Ideally we should be able to decide whether to create a new tree, + // or go deeper or higher in the stack. + return last_function_call->GetLastTracedSegment().CreateNestedCall( + cursor_sp, symbol_info); + } +} + +/// Append an error to a function call forest. The new error might be appended +/// to the current segment if it contains errors or will create a new root. +/// +/// \param[in] last_function_call +/// The chronologically most recent function call before the new error. +/// +/// \param[in] cursor_sp +/// The cursor pointing to the new error. +/// +/// \param[in,out] roots +/// The object owning the roots. It might be modified if a new root needs to +/// be created. +/// +/// \return +/// A reference to the function call that owns the new error. +TraceDumper::FunctionCall &AppendErrorToFunctionCallForest( + TraceDumper::FunctionCall *last_function_call, TraceCursorSP &cursor_sp, + std::vector<TraceDumper::FunctionCallUP> &roots) { + if (last_function_call && last_function_call->IsError()) { + last_function_call->GetLastTracedSegment().AppendInsn( + cursor_sp, TraceDumper::SymbolInfo{}); + return *last_function_call; + } else { + roots.emplace_back(std::make_unique<TraceDumper::FunctionCall>( + cursor_sp, TraceDumper::SymbolInfo{})); + return *roots.back(); + } +} + +static std::vector<TraceDumper::FunctionCallUP> +CreateFunctionCallForest(TraceCursorSP &cursor_sp, + const ExecutionContext &exe_ctx) { + + std::vector<TraceDumper::FunctionCallUP> roots; + TraceDumper::SymbolInfo prev_symbol_info; + + TraceDumper::FunctionCall *last_function_call = nullptr; + + for (; cursor_sp->HasValue(); cursor_sp->Next()) { + if (cursor_sp->IsError()) { + last_function_call = &AppendErrorToFunctionCallForest(last_function_call, + cursor_sp, roots); + prev_symbol_info = {}; + } else if (cursor_sp->IsInstruction()) { + TraceDumper::SymbolInfo symbol_info = CalculateSymbolInfo( + exe_ctx, cursor_sp->GetLoadAddress(), prev_symbol_info); + + last_function_call = &AppendInstructionToFunctionCallForest( + exe_ctx, last_function_call, prev_symbol_info, symbol_info, cursor_sp, + roots); + prev_symbol_info = symbol_info; + } else if (cursor_sp->GetEventType() == eTraceEventCPUChanged) { + // TODO: In case of a CPU change, we create a new root because we haven't + // investigated yet if a call tree can safely continue or if interrupts + // could have polluted the original call tree. + last_function_call = nullptr; + prev_symbol_info = {}; + } + } + + return roots; +} + +void TraceDumper::DumpFunctionCalls() { + ThreadSP thread_sp = m_cursor_sp->GetExecutionContextRef().GetThreadSP(); + ExecutionContext exe_ctx; + thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx); + + m_writer_up->FunctionCallForest( + CreateFunctionCallForest(m_cursor_sp, exe_ctx)); +} diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp index 3ece3ee24cbe..858b1691f318 100644 --- a/lldb/source/Target/UnixSignals.cpp +++ b/lldb/source/Target/UnixSignals.cpp @@ -13,6 +13,7 @@ #include "Plugins/Process/Utility/NetBSDSignals.h" #include "lldb/Host/HostInfo.h" #include "lldb/Utility/ArchSpec.h" +#include <optional> using namespace lldb_private; using namespace llvm; @@ -285,9 +286,9 @@ int32_t UnixSignals::GetSignalAtIndex(int32_t index) const { uint64_t UnixSignals::GetVersion() const { return m_version; } std::vector<int32_t> -UnixSignals::GetFilteredSignals(llvm::Optional<bool> should_suppress, - llvm::Optional<bool> should_stop, - llvm::Optional<bool> should_notify) { +UnixSignals::GetFilteredSignals(std::optional<bool> should_suppress, + std::optional<bool> should_stop, + std::optional<bool> should_notify) { std::vector<int32_t> result; for (int32_t signo = GetFirstSignalNumber(); signo != LLDB_INVALID_SIGNAL_NUMBER; @@ -300,13 +301,13 @@ UnixSignals::GetFilteredSignals(llvm::Optional<bool> should_suppress, // If any of filtering conditions are not met, we move on to the next // signal. - if (should_suppress && signal_suppress != should_suppress.value()) + if (should_suppress && signal_suppress != *should_suppress) continue; - if (should_stop && signal_stop != should_stop.value()) + if (should_stop && signal_stop != *should_stop) continue; - if (should_notify && signal_notify != should_notify.value()) + if (should_notify && signal_notify != *should_notify) continue; result.push_back(signo); |
