aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Core
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Core')
-rw-r--r--contrib/llvm-project/lldb/source/Core/Address.cpp1019
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressRange.cpp211
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressResolver.cpp43
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp84
-rw-r--r--contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp198
-rw-r--r--contrib/llvm-project/lldb/source/Core/Communication.cpp431
-rw-r--r--contrib/llvm-project/lldb/source/Core/CoreProperties.td118
-rw-r--r--contrib/llvm-project/lldb/source/Core/Debugger.cpp1629
-rw-r--r--contrib/llvm-project/lldb/source/Core/Disassembler.cpp1466
-rw-r--r--contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp813
-rw-r--r--contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp78
-rw-r--r--contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp245
-rw-r--r--contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp585
-rw-r--r--contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp89
-rw-r--r--contrib/llvm-project/lldb/source/Core/FileSpecList.cpp121
-rw-r--r--contrib/llvm-project/lldb/source/Core/FormatEntity.cpp2414
-rw-r--r--contrib/llvm-project/lldb/source/Core/Highlighter.cpp81
-rw-r--r--contrib/llvm-project/lldb/source/Core/IOHandler.cpp4625
-rw-r--r--contrib/llvm-project/lldb/source/Core/Mangled.cpp438
-rw-r--r--contrib/llvm-project/lldb/source/Core/Module.cpp1633
-rw-r--r--contrib/llvm-project/lldb/source/Core/ModuleChild.cpp28
-rw-r--r--contrib/llvm-project/lldb/source/Core/ModuleList.cpp1003
-rw-r--r--contrib/llvm-project/lldb/source/Core/Opcode.cpp140
-rw-r--r--contrib/llvm-project/lldb/source/Core/PluginManager.cpp2599
-rw-r--r--contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp172
-rw-r--r--contrib/llvm-project/lldb/source/Core/SearchFilter.cpp852
-rw-r--r--contrib/llvm-project/lldb/source/Core/Section.cpp624
-rw-r--r--contrib/llvm-project/lldb/source/Core/SourceManager.cpp701
-rw-r--r--contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp36
-rw-r--r--contrib/llvm-project/lldb/source/Core/StreamFile.cpp70
-rw-r--r--contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp113
-rw-r--r--contrib/llvm-project/lldb/source/Core/Value.cpp707
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObject.cpp3370
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp94
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp252
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp296
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp62
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp74
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp180
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp389
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp110
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp228
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp363
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp394
-rw-r--r--contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp376
45 files changed, 29554 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Core/Address.cpp b/contrib/llvm-project/lldb/source/Core/Address.cpp
new file mode 100644
index 000000000000..a3912bef5a6e
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Address.cpp
@@ -0,0 +1,1019 @@
+//===-- Address.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+
+namespace lldb_private {
+class CompileUnit;
+}
+namespace lldb_private {
+class Function;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static size_t ReadBytes(ExecutionContextScope *exe_scope,
+ const Address &address, void *dst, size_t dst_len) {
+ if (exe_scope == nullptr)
+ return 0;
+
+ TargetSP target_sp(exe_scope->CalculateTarget());
+ if (target_sp) {
+ Status error;
+ bool prefer_file_cache = false;
+ return target_sp->ReadMemory(address, prefer_file_cache, dst, dst_len,
+ error);
+ }
+ return 0;
+}
+
+static bool GetByteOrderAndAddressSize(ExecutionContextScope *exe_scope,
+ const Address &address,
+ ByteOrder &byte_order,
+ uint32_t &addr_size) {
+ byte_order = eByteOrderInvalid;
+ addr_size = 0;
+ if (exe_scope == nullptr)
+ return false;
+
+ TargetSP target_sp(exe_scope->CalculateTarget());
+ if (target_sp) {
+ byte_order = target_sp->GetArchitecture().GetByteOrder();
+ addr_size = target_sp->GetArchitecture().GetAddressByteSize();
+ }
+
+ if (byte_order == eByteOrderInvalid || addr_size == 0) {
+ ModuleSP module_sp(address.GetModule());
+ if (module_sp) {
+ byte_order = module_sp->GetArchitecture().GetByteOrder();
+ addr_size = module_sp->GetArchitecture().GetAddressByteSize();
+ }
+ }
+ return byte_order != eByteOrderInvalid && addr_size != 0;
+}
+
+static uint64_t ReadUIntMax64(ExecutionContextScope *exe_scope,
+ const Address &address, uint32_t byte_size,
+ bool &success) {
+ uint64_t uval64 = 0;
+ if (exe_scope == nullptr || byte_size > sizeof(uint64_t)) {
+ success = false;
+ return 0;
+ }
+ uint64_t buf = 0;
+
+ success = ReadBytes(exe_scope, address, &buf, byte_size) == byte_size;
+ if (success) {
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t addr_size = 0;
+ if (GetByteOrderAndAddressSize(exe_scope, address, byte_order, addr_size)) {
+ DataExtractor data(&buf, sizeof(buf), byte_order, addr_size);
+ lldb::offset_t offset = 0;
+ uval64 = data.GetU64(&offset);
+ } else
+ success = false;
+ }
+ return uval64;
+}
+
+static bool ReadAddress(ExecutionContextScope *exe_scope,
+ const Address &address, uint32_t pointer_size,
+ Address &deref_so_addr) {
+ if (exe_scope == nullptr)
+ return false;
+
+ bool success = false;
+ addr_t deref_addr = ReadUIntMax64(exe_scope, address, pointer_size, success);
+ if (success) {
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ // If we have any sections that are loaded, try and resolve using the
+ // section load list
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target && !target->GetSectionLoadList().IsEmpty()) {
+ if (target->GetSectionLoadList().ResolveLoadAddress(deref_addr,
+ deref_so_addr))
+ return true;
+ } else {
+ // If we were not running, yet able to read an integer, we must have a
+ // module
+ ModuleSP module_sp(address.GetModule());
+
+ assert(module_sp);
+ if (module_sp->ResolveFileAddress(deref_addr, deref_so_addr))
+ return true;
+ }
+
+ // We couldn't make "deref_addr" into a section offset value, but we were
+ // able to read the address, so we return a section offset address with no
+ // section and "deref_addr" as the offset (address).
+ deref_so_addr.SetRawAddress(deref_addr);
+ return true;
+ }
+ return false;
+}
+
+static bool DumpUInt(ExecutionContextScope *exe_scope, const Address &address,
+ uint32_t byte_size, Stream *strm) {
+ if (exe_scope == nullptr || byte_size == 0)
+ return false;
+ std::vector<uint8_t> buf(byte_size, 0);
+
+ if (ReadBytes(exe_scope, address, &buf[0], buf.size()) == buf.size()) {
+ ByteOrder byte_order = eByteOrderInvalid;
+ uint32_t addr_size = 0;
+ if (GetByteOrderAndAddressSize(exe_scope, address, byte_order, addr_size)) {
+ DataExtractor data(&buf.front(), buf.size(), byte_order, addr_size);
+
+ DumpDataExtractor(data, strm,
+ 0, // Start offset in "data"
+ eFormatHex, // Print as characters
+ buf.size(), // Size of item
+ 1, // Items count
+ UINT32_MAX, // num per line
+ LLDB_INVALID_ADDRESS, // base address
+ 0, // bitfield bit size
+ 0); // bitfield bit offset
+
+ return true;
+ }
+ }
+ return false;
+}
+
+static size_t ReadCStringFromMemory(ExecutionContextScope *exe_scope,
+ const Address &address, Stream *strm) {
+ if (exe_scope == nullptr)
+ return 0;
+ const size_t k_buf_len = 256;
+ char buf[k_buf_len + 1];
+ buf[k_buf_len] = '\0'; // NULL terminate
+
+ // Byte order and address size don't matter for C string dumping..
+ DataExtractor data(buf, sizeof(buf), endian::InlHostByteOrder(), 4);
+ size_t total_len = 0;
+ size_t bytes_read;
+ Address curr_address(address);
+ strm->PutChar('"');
+ while ((bytes_read = ReadBytes(exe_scope, curr_address, buf, k_buf_len)) >
+ 0) {
+ size_t len = strlen(buf);
+ if (len == 0)
+ break;
+ if (len > bytes_read)
+ len = bytes_read;
+
+ DumpDataExtractor(data, strm,
+ 0, // Start offset in "data"
+ eFormatChar, // Print as characters
+ 1, // Size of item (1 byte for a char!)
+ len, // How many bytes to print?
+ UINT32_MAX, // num per line
+ LLDB_INVALID_ADDRESS, // base address
+ 0, // bitfield bit size
+
+ 0); // bitfield bit offset
+
+ total_len += bytes_read;
+
+ if (len < k_buf_len)
+ break;
+ curr_address.SetOffset(curr_address.GetOffset() + bytes_read);
+ }
+ strm->PutChar('"');
+ return total_len;
+}
+
+Address::Address(lldb::addr_t abs_addr) : m_section_wp(), m_offset(abs_addr) {}
+
+Address::Address(addr_t address, const SectionList *section_list)
+ : m_section_wp(), m_offset(LLDB_INVALID_ADDRESS) {
+ ResolveAddressUsingFileSections(address, section_list);
+}
+
+const Address &Address::operator=(const Address &rhs) {
+ if (this != &rhs) {
+ m_section_wp = rhs.m_section_wp;
+ m_offset = rhs.m_offset;
+ }
+ return *this;
+}
+
+bool Address::ResolveAddressUsingFileSections(addr_t file_addr,
+ const SectionList *section_list) {
+ if (section_list) {
+ SectionSP section_sp(
+ section_list->FindSectionContainingFileAddress(file_addr));
+ m_section_wp = section_sp;
+ if (section_sp) {
+ assert(section_sp->ContainsFileAddress(file_addr));
+ m_offset = file_addr - section_sp->GetFileAddress();
+ return true; // Successfully transformed addr into a section offset
+ // address
+ }
+ }
+ m_offset = file_addr;
+ return false; // Failed to resolve this address to a section offset value
+}
+
+/// if "addr_range_ptr" is not NULL, then fill in with the address range of the function.
+bool Address::ResolveFunctionScope(SymbolContext &sym_ctx,
+ AddressRange *addr_range_ptr) {
+ constexpr SymbolContextItem resolve_scope =
+ eSymbolContextFunction | eSymbolContextSymbol;
+
+ if (!(CalculateSymbolContext(&sym_ctx, resolve_scope) & resolve_scope)) {
+ if (addr_range_ptr)
+ addr_range_ptr->Clear();
+ return false;
+ }
+
+ if (!addr_range_ptr)
+ return true;
+
+ return sym_ctx.GetAddressRange(resolve_scope, 0, false, *addr_range_ptr);
+}
+
+ModuleSP Address::GetModule() const {
+ lldb::ModuleSP module_sp;
+ SectionSP section_sp(GetSection());
+ if (section_sp)
+ module_sp = section_sp->GetModule();
+ return module_sp;
+}
+
+addr_t Address::GetFileAddress() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ addr_t sect_file_addr = section_sp->GetFileAddress();
+ if (sect_file_addr == LLDB_INVALID_ADDRESS) {
+ // Section isn't resolved, we can't return a valid file address
+ return LLDB_INVALID_ADDRESS;
+ }
+ // We have a valid file range, so we can return the file based address by
+ // adding the file base address to our offset
+ return sect_file_addr + m_offset;
+ } else if (SectionWasDeletedPrivate()) {
+ // Used to have a valid section but it got deleted so the offset doesn't
+ // mean anything without the section
+ return LLDB_INVALID_ADDRESS;
+ }
+ // No section, we just return the offset since it is the value in this case
+ return m_offset;
+}
+
+addr_t Address::GetLoadAddress(Target *target) const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ if (target) {
+ addr_t sect_load_addr = section_sp->GetLoadBaseAddress(target);
+
+ if (sect_load_addr != LLDB_INVALID_ADDRESS) {
+ // We have a valid file range, so we can return the file based address
+ // by adding the file base address to our offset
+ return sect_load_addr + m_offset;
+ }
+ }
+ } else if (SectionWasDeletedPrivate()) {
+ // Used to have a valid section but it got deleted so the offset doesn't
+ // mean anything without the section
+ return LLDB_INVALID_ADDRESS;
+ } else {
+ // We don't have a section so the offset is the load address
+ return m_offset;
+ }
+ // The section isn't resolved or an invalid target was passed in so we can't
+ // return a valid load address.
+ return LLDB_INVALID_ADDRESS;
+}
+
+addr_t Address::GetCallableLoadAddress(Target *target, bool is_indirect) const {
+ addr_t code_addr = LLDB_INVALID_ADDRESS;
+
+ if (is_indirect && target) {
+ ProcessSP processSP = target->GetProcessSP();
+ Status error;
+ if (processSP) {
+ code_addr = processSP->ResolveIndirectFunction(this, error);
+ if (!error.Success())
+ code_addr = LLDB_INVALID_ADDRESS;
+ }
+ } else {
+ code_addr = GetLoadAddress(target);
+ }
+
+ if (code_addr == LLDB_INVALID_ADDRESS)
+ return code_addr;
+
+ if (target)
+ return target->GetCallableLoadAddress(code_addr, GetAddressClass());
+ return code_addr;
+}
+
+bool Address::SetCallableLoadAddress(lldb::addr_t load_addr, Target *target) {
+ if (SetLoadAddress(load_addr, target)) {
+ if (target)
+ m_offset = target->GetCallableLoadAddress(m_offset, GetAddressClass());
+ return true;
+ }
+ return false;
+}
+
+addr_t Address::GetOpcodeLoadAddress(Target *target,
+ AddressClass addr_class) const {
+ addr_t code_addr = GetLoadAddress(target);
+ if (code_addr != LLDB_INVALID_ADDRESS) {
+ if (addr_class == AddressClass::eInvalid)
+ addr_class = GetAddressClass();
+ code_addr = target->GetOpcodeLoadAddress(code_addr, addr_class);
+ }
+ return code_addr;
+}
+
+bool Address::SetOpcodeLoadAddress(lldb::addr_t load_addr, Target *target,
+ AddressClass addr_class,
+ bool allow_section_end) {
+ if (SetLoadAddress(load_addr, target, allow_section_end)) {
+ if (target) {
+ if (addr_class == AddressClass::eInvalid)
+ addr_class = GetAddressClass();
+ m_offset = target->GetOpcodeLoadAddress(m_offset, addr_class);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
+ DumpStyle fallback_style, uint32_t addr_size) const {
+ // If the section was nullptr, only load address is going to work unless we
+ // are trying to deref a pointer
+ SectionSP section_sp(GetSection());
+ if (!section_sp && style != DumpStyleResolvedPointerDescription)
+ style = DumpStyleLoadAddress;
+
+ ExecutionContext exe_ctx(exe_scope);
+ Target *target = exe_ctx.GetTargetPtr();
+ // If addr_byte_size is UINT32_MAX, then determine the correct address byte
+ // size for the process or default to the size of addr_t
+ if (addr_size == UINT32_MAX) {
+ if (target)
+ addr_size = target->GetArchitecture().GetAddressByteSize();
+ else
+ addr_size = sizeof(addr_t);
+ }
+
+ Address so_addr;
+ switch (style) {
+ case DumpStyleInvalid:
+ return false;
+
+ case DumpStyleSectionNameOffset:
+ if (section_sp) {
+ section_sp->DumpName(s);
+ s->Printf(" + %" PRIu64, m_offset);
+ } else {
+ s->Address(m_offset, addr_size);
+ }
+ break;
+
+ case DumpStyleSectionPointerOffset:
+ s->Printf("(Section *)%p + ", static_cast<void *>(section_sp.get()));
+ s->Address(m_offset, addr_size);
+ break;
+
+ case DumpStyleModuleWithFileAddress:
+ if (section_sp) {
+ ModuleSP module_sp = section_sp->GetModule();
+ if (module_sp)
+ s->Printf("%s[", module_sp->GetFileSpec().GetFilename().AsCString(
+ "<Unknown>"));
+ else
+ s->Printf("%s[", "<Unknown>");
+ }
+ LLVM_FALLTHROUGH;
+ case DumpStyleFileAddress: {
+ addr_t file_addr = GetFileAddress();
+ if (file_addr == LLDB_INVALID_ADDRESS) {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ s->Address(file_addr, addr_size);
+ if (style == DumpStyleModuleWithFileAddress && section_sp)
+ s->PutChar(']');
+ } break;
+
+ case DumpStyleLoadAddress: {
+ addr_t load_addr = GetLoadAddress(target);
+
+ /*
+ * MIPS:
+ * Display address in compressed form for MIPS16 or microMIPS
+ * if the address belongs to AddressClass::eCodeAlternateISA.
+ */
+ if (target) {
+ const llvm::Triple::ArchType llvm_arch =
+ target->GetArchitecture().GetMachine();
+ if (llvm_arch == llvm::Triple::mips ||
+ llvm_arch == llvm::Triple::mipsel ||
+ llvm_arch == llvm::Triple::mips64 ||
+ llvm_arch == llvm::Triple::mips64el)
+ load_addr = GetCallableLoadAddress(target);
+ }
+
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ s->Address(load_addr, addr_size);
+ } break;
+
+ case DumpStyleResolvedDescription:
+ case DumpStyleResolvedDescriptionNoModule:
+ case DumpStyleResolvedDescriptionNoFunctionArguments:
+ case DumpStyleNoFunctionName:
+ if (IsSectionOffset()) {
+ uint32_t pointer_size = 4;
+ ModuleSP module_sp(GetModule());
+ if (target)
+ pointer_size = target->GetArchitecture().GetAddressByteSize();
+ else if (module_sp)
+ pointer_size = module_sp->GetArchitecture().GetAddressByteSize();
+
+ bool showed_info = false;
+ if (section_sp) {
+ SectionType sect_type = section_sp->GetType();
+ switch (sect_type) {
+ case eSectionTypeData:
+ if (module_sp) {
+ if (Symtab *symtab = module_sp->GetSymtab()) {
+ const addr_t file_Addr = GetFileAddress();
+ Symbol *symbol =
+ symtab->FindSymbolContainingFileAddress(file_Addr);
+ if (symbol) {
+ const char *symbol_name = symbol->GetName().AsCString();
+ if (symbol_name) {
+ s->PutCString(symbol_name);
+ addr_t delta =
+ file_Addr - symbol->GetAddressRef().GetFileAddress();
+ if (delta)
+ s->Printf(" + %" PRIu64, delta);
+ showed_info = true;
+ }
+ }
+ }
+ }
+ break;
+
+ case eSectionTypeDataCString:
+ // Read the C string from memory and display it
+ showed_info = true;
+ ReadCStringFromMemory(exe_scope, *this, s);
+ break;
+
+ case eSectionTypeDataCStringPointers:
+ if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
+#if VERBOSE_OUTPUT
+ s->PutCString("(char *)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+ s->PutCString(": ");
+#endif
+ showed_info = true;
+ ReadCStringFromMemory(exe_scope, so_addr, s);
+ }
+ break;
+
+ case eSectionTypeDataObjCMessageRefs:
+ if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
+ if (target && so_addr.IsSectionOffset()) {
+ SymbolContext func_sc;
+ target->GetImages().ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, func_sc);
+ if (func_sc.function != nullptr || func_sc.symbol != nullptr) {
+ showed_info = true;
+#if VERBOSE_OUTPUT
+ s->PutCString("(objc_msgref *) -> { (func*)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+#else
+ s->PutCString("{ ");
+#endif
+ Address cstr_addr(*this);
+ cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size);
+ func_sc.DumpStopContext(s, exe_scope, so_addr, true, true,
+ false, true, true);
+ if (ReadAddress(exe_scope, cstr_addr, pointer_size, so_addr)) {
+#if VERBOSE_OUTPUT
+ s->PutCString("), (char *)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+ s->PutCString(" (");
+#else
+ s->PutCString(", ");
+#endif
+ ReadCStringFromMemory(exe_scope, so_addr, s);
+ }
+#if VERBOSE_OUTPUT
+ s->PutCString(") }");
+#else
+ s->PutCString(" }");
+#endif
+ }
+ }
+ }
+ break;
+
+ case eSectionTypeDataObjCCFStrings: {
+ Address cfstring_data_addr(*this);
+ cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() +
+ (2 * pointer_size));
+ if (ReadAddress(exe_scope, cfstring_data_addr, pointer_size,
+ so_addr)) {
+#if VERBOSE_OUTPUT
+ s->PutCString("(CFString *) ");
+ cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+ s->PutCString(" -> @");
+#else
+ s->PutChar('@');
+#endif
+ if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription))
+ showed_info = true;
+ }
+ } break;
+
+ case eSectionTypeData4:
+ // Read the 4 byte data and display it
+ showed_info = true;
+ s->PutCString("(uint32_t) ");
+ DumpUInt(exe_scope, *this, 4, s);
+ break;
+
+ case eSectionTypeData8:
+ // Read the 8 byte data and display it
+ showed_info = true;
+ s->PutCString("(uint64_t) ");
+ DumpUInt(exe_scope, *this, 8, s);
+ break;
+
+ case eSectionTypeData16:
+ // Read the 16 byte data and display it
+ showed_info = true;
+ s->PutCString("(uint128_t) ");
+ DumpUInt(exe_scope, *this, 16, s);
+ break;
+
+ case eSectionTypeDataPointers:
+ // Read the pointer data and display it
+ if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) {
+ s->PutCString("(void *)");
+ so_addr.Dump(s, exe_scope, DumpStyleLoadAddress,
+ DumpStyleFileAddress);
+
+ showed_info = true;
+ if (so_addr.IsSectionOffset()) {
+ SymbolContext pointer_sc;
+ if (target) {
+ target->GetImages().ResolveSymbolContextForAddress(
+ so_addr, eSymbolContextEverything, pointer_sc);
+ if (pointer_sc.function != nullptr ||
+ pointer_sc.symbol != nullptr) {
+ s->PutCString(": ");
+ pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false,
+ false, true, true);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!showed_info) {
+ if (module_sp) {
+ SymbolContext sc;
+ module_sp->ResolveSymbolContextForAddress(
+ *this, eSymbolContextEverything, sc);
+ if (sc.function || sc.symbol) {
+ bool show_stop_context = true;
+ const bool show_module = (style == DumpStyleResolvedDescription);
+ const bool show_fullpaths = false;
+ const bool show_inlined_frames = true;
+ const bool show_function_arguments =
+ (style != DumpStyleResolvedDescriptionNoFunctionArguments);
+ const bool show_function_name = (style != DumpStyleNoFunctionName);
+ if (sc.function == nullptr && sc.symbol != nullptr) {
+ // If we have just a symbol make sure it is in the right section
+ if (sc.symbol->ValueIsAddress()) {
+ if (sc.symbol->GetAddressRef().GetSection() != GetSection()) {
+ // don't show the module if the symbol is a trampoline symbol
+ show_stop_context = false;
+ }
+ }
+ }
+ if (show_stop_context) {
+ // We have a function or a symbol from the same sections as this
+ // address.
+ sc.DumpStopContext(s, exe_scope, *this, show_fullpaths,
+ show_module, show_inlined_frames,
+ show_function_arguments, show_function_name);
+ } else {
+ // We found a symbol but it was in a different section so it
+ // isn't the symbol we should be showing, just show the section
+ // name + offset
+ Dump(s, exe_scope, DumpStyleSectionNameOffset);
+ }
+ }
+ }
+ }
+ } else {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ break;
+
+ case DumpStyleDetailedSymbolContext:
+ if (IsSectionOffset()) {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ SymbolContext sc;
+ module_sp->ResolveSymbolContextForAddress(
+ *this, eSymbolContextEverything | eSymbolContextVariable, sc);
+ if (sc.symbol) {
+ // If we have just a symbol make sure it is in the same section as
+ // our address. If it isn't, then we might have just found the last
+ // symbol that came before the address that we are looking up that
+ // has nothing to do with our address lookup.
+ if (sc.symbol->ValueIsAddress() &&
+ sc.symbol->GetAddressRef().GetSection() != GetSection())
+ sc.symbol = nullptr;
+ }
+ sc.GetDescription(s, eDescriptionLevelBrief, target);
+
+ if (sc.block) {
+ bool can_create = true;
+ bool get_parent_variables = true;
+ bool stop_if_block_is_inlined_function = false;
+ VariableList variable_list;
+ sc.block->AppendVariables(can_create, get_parent_variables,
+ stop_if_block_is_inlined_function,
+ [](Variable *) { return true; },
+ &variable_list);
+
+ const size_t num_variables = variable_list.GetSize();
+ for (size_t var_idx = 0; var_idx < num_variables; ++var_idx) {
+ Variable *var = variable_list.GetVariableAtIndex(var_idx).get();
+ if (var && var->LocationIsValidForAddress(*this)) {
+ s->Indent();
+ s->Printf(" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\"",
+ var->GetID(), var->GetName().GetCString());
+ Type *type = var->GetType();
+ if (type)
+ s->Printf(", type = \"%s\"", type->GetName().GetCString());
+ else
+ s->PutCString(", type = <unknown>");
+ s->PutCString(", location = ");
+ var->DumpLocationForAddress(s, *this);
+ s->PutCString(", decl = ");
+ var->GetDeclaration().DumpStopContext(s, false);
+ s->EOL();
+ }
+ }
+ }
+ }
+ } else {
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ }
+ break;
+
+ case DumpStyleResolvedPointerDescription: {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ addr_t load_addr = GetLoadAddress(target);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ Status memory_error;
+ addr_t dereferenced_load_addr =
+ process->ReadPointerFromMemory(load_addr, memory_error);
+ if (dereferenced_load_addr != LLDB_INVALID_ADDRESS) {
+ Address dereferenced_addr;
+ if (dereferenced_addr.SetLoadAddress(dereferenced_load_addr,
+ target)) {
+ StreamString strm;
+ if (dereferenced_addr.Dump(&strm, exe_scope,
+ DumpStyleResolvedDescription,
+ DumpStyleInvalid, addr_size)) {
+ s->Address(dereferenced_load_addr, addr_size, " -> ", " ");
+ s->Write(strm.GetString().data(), strm.GetSize());
+ return true;
+ }
+ }
+ }
+ }
+ }
+ if (fallback_style != DumpStyleInvalid)
+ return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
+ return false;
+ } break;
+ }
+
+ return true;
+}
+
+bool Address::SectionWasDeleted() const {
+ if (GetSection())
+ return false;
+ return SectionWasDeletedPrivate();
+}
+
+bool Address::SectionWasDeletedPrivate() const {
+ lldb::SectionWP empty_section_wp;
+
+ // If either call to "std::weak_ptr::owner_before(...) value returns true,
+ // this indicates that m_section_wp once contained (possibly still does) a
+ // reference to a valid shared pointer. This helps us know if we had a valid
+ // reference to a section which is now invalid because the module it was in
+ // was unloaded/deleted, or if the address doesn't have a valid reference to
+ // a section.
+ return empty_section_wp.owner_before(m_section_wp) ||
+ m_section_wp.owner_before(empty_section_wp);
+}
+
+uint32_t
+Address::CalculateSymbolContext(SymbolContext *sc,
+ SymbolContextItem resolve_scope) const {
+ sc->Clear(false);
+ // Absolute addresses don't have enough information to reconstruct even their
+ // target.
+
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ ModuleSP module_sp(section_sp->GetModule());
+ if (module_sp) {
+ sc->module_sp = module_sp;
+ if (sc->module_sp)
+ return sc->module_sp->ResolveSymbolContextForAddress(
+ *this, resolve_scope, *sc);
+ }
+ }
+ return 0;
+}
+
+ModuleSP Address::CalculateSymbolContextModule() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp)
+ return section_sp->GetModule();
+ return ModuleSP();
+}
+
+CompileUnit *Address::CalculateSymbolContextCompileUnit() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this,
+ eSymbolContextCompUnit, sc);
+ return sc.comp_unit;
+ }
+ }
+ return nullptr;
+}
+
+Function *Address::CalculateSymbolContextFunction() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this,
+ eSymbolContextFunction, sc);
+ return sc.function;
+ }
+ }
+ return nullptr;
+}
+
+Block *Address::CalculateSymbolContextBlock() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextBlock,
+ sc);
+ return sc.block;
+ }
+ }
+ return nullptr;
+}
+
+Symbol *Address::CalculateSymbolContextSymbol() const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextSymbol,
+ sc);
+ return sc.symbol;
+ }
+ }
+ return nullptr;
+}
+
+bool Address::CalculateSymbolContextLineEntry(LineEntry &line_entry) const {
+ SectionSP section_sp(GetSection());
+ if (section_sp) {
+ SymbolContext sc;
+ sc.module_sp = section_sp->GetModule();
+ if (sc.module_sp) {
+ sc.module_sp->ResolveSymbolContextForAddress(*this,
+ eSymbolContextLineEntry, sc);
+ if (sc.line_entry.IsValid()) {
+ line_entry = sc.line_entry;
+ return true;
+ }
+ }
+ }
+ line_entry.Clear();
+ return false;
+}
+
+int Address::CompareFileAddress(const Address &a, const Address &b) {
+ addr_t a_file_addr = a.GetFileAddress();
+ addr_t b_file_addr = b.GetFileAddress();
+ if (a_file_addr < b_file_addr)
+ return -1;
+ if (a_file_addr > b_file_addr)
+ return +1;
+ return 0;
+}
+
+int Address::CompareLoadAddress(const Address &a, const Address &b,
+ Target *target) {
+ assert(target != nullptr);
+ addr_t a_load_addr = a.GetLoadAddress(target);
+ addr_t b_load_addr = b.GetLoadAddress(target);
+ if (a_load_addr < b_load_addr)
+ return -1;
+ if (a_load_addr > b_load_addr)
+ return +1;
+ return 0;
+}
+
+int Address::CompareModulePointerAndOffset(const Address &a, const Address &b) {
+ ModuleSP a_module_sp(a.GetModule());
+ ModuleSP b_module_sp(b.GetModule());
+ Module *a_module = a_module_sp.get();
+ Module *b_module = b_module_sp.get();
+ if (a_module < b_module)
+ return -1;
+ if (a_module > b_module)
+ return +1;
+ // Modules are the same, just compare the file address since they should be
+ // unique
+ addr_t a_file_addr = a.GetFileAddress();
+ addr_t b_file_addr = b.GetFileAddress();
+ if (a_file_addr < b_file_addr)
+ return -1;
+ if (a_file_addr > b_file_addr)
+ return +1;
+ return 0;
+}
+
+size_t Address::MemorySize() const {
+ // Noting special for the memory size of a single Address object, it is just
+ // the size of itself.
+ return sizeof(Address);
+}
+
+// NOTE: Be careful using this operator. It can correctly compare two
+// addresses from the same Module correctly. It can't compare two addresses
+// from different modules in any meaningful way, but it will compare the module
+// pointers.
+//
+// To sum things up:
+// - works great for addresses within the same module - it works for addresses
+// across multiple modules, but don't expect the
+// address results to make much sense
+//
+// This basically lets Address objects be used in ordered collection classes.
+
+bool lldb_private::operator<(const Address &lhs, const Address &rhs) {
+ ModuleSP lhs_module_sp(lhs.GetModule());
+ ModuleSP rhs_module_sp(rhs.GetModule());
+ Module *lhs_module = lhs_module_sp.get();
+ Module *rhs_module = rhs_module_sp.get();
+ if (lhs_module == rhs_module) {
+ // Addresses are in the same module, just compare the file addresses
+ return lhs.GetFileAddress() < rhs.GetFileAddress();
+ } else {
+ // The addresses are from different modules, just use the module pointer
+ // value to get consistent ordering
+ return lhs_module < rhs_module;
+ }
+}
+
+bool lldb_private::operator>(const Address &lhs, const Address &rhs) {
+ ModuleSP lhs_module_sp(lhs.GetModule());
+ ModuleSP rhs_module_sp(rhs.GetModule());
+ Module *lhs_module = lhs_module_sp.get();
+ Module *rhs_module = rhs_module_sp.get();
+ if (lhs_module == rhs_module) {
+ // Addresses are in the same module, just compare the file addresses
+ return lhs.GetFileAddress() > rhs.GetFileAddress();
+ } else {
+ // The addresses are from different modules, just use the module pointer
+ // value to get consistent ordering
+ return lhs_module > rhs_module;
+ }
+}
+
+// The operator == checks for exact equality only (same section, same offset)
+bool lldb_private::operator==(const Address &a, const Address &rhs) {
+ return a.GetOffset() == rhs.GetOffset() && a.GetSection() == rhs.GetSection();
+}
+
+// The operator != checks for exact inequality only (differing section, or
+// different offset)
+bool lldb_private::operator!=(const Address &a, const Address &rhs) {
+ return a.GetOffset() != rhs.GetOffset() || a.GetSection() != rhs.GetSection();
+}
+
+AddressClass Address::GetAddressClass() const {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ ObjectFile *obj_file = module_sp->GetObjectFile();
+ if (obj_file) {
+ // Give the symbol file a chance to add to the unified section list
+ // and to the symtab.
+ module_sp->GetSymtab();
+ return obj_file->GetAddressClass(GetFileAddress());
+ }
+ }
+ return AddressClass::eUnknown;
+}
+
+bool Address::SetLoadAddress(lldb::addr_t load_addr, Target *target,
+ bool allow_section_end) {
+ if (target && target->GetSectionLoadList().ResolveLoadAddress(
+ load_addr, *this, allow_section_end))
+ return true;
+ m_section_wp.reset();
+ m_offset = load_addr;
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressRange.cpp b/contrib/llvm-project/lldb/source/Core/AddressRange.cpp
new file mode 100644
index 000000000000..71eec3c19607
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressRange.cpp
@@ -0,0 +1,211 @@
+//===-- AddressRange.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-defines.h"
+
+#include "llvm/Support/Compiler.h"
+
+#include <memory>
+
+#include <inttypes.h>
+
+namespace lldb_private {
+class SectionList;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+AddressRange::AddressRange() : m_base_addr(), m_byte_size(0) {}
+
+AddressRange::AddressRange(addr_t file_addr, addr_t byte_size,
+ const SectionList *section_list)
+ : m_base_addr(file_addr, section_list), m_byte_size(byte_size) {}
+
+AddressRange::AddressRange(const lldb::SectionSP &section, addr_t offset,
+ addr_t byte_size)
+ : m_base_addr(section, offset), m_byte_size(byte_size) {}
+
+AddressRange::AddressRange(const Address &so_addr, addr_t byte_size)
+ : m_base_addr(so_addr), m_byte_size(byte_size) {}
+
+AddressRange::~AddressRange() {}
+
+// bool
+// AddressRange::Contains (const Address &addr) const
+//{
+// const addr_t byte_size = GetByteSize();
+// if (byte_size)
+// return addr.GetSection() == m_base_addr.GetSection() &&
+// (addr.GetOffset() - m_base_addr.GetOffset()) < byte_size;
+//}
+//
+// bool
+// AddressRange::Contains (const Address *addr) const
+//{
+// if (addr)
+// return Contains (*addr);
+// return false;
+//}
+
+bool AddressRange::ContainsFileAddress(const Address &addr) const {
+ if (addr.GetSection() == m_base_addr.GetSection())
+ return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize();
+ addr_t file_base_addr = GetBaseAddress().GetFileAddress();
+ if (file_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t file_addr = addr.GetFileAddress();
+ if (file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (file_base_addr <= file_addr)
+ return (file_addr - file_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::ContainsFileAddress(addr_t file_addr) const {
+ if (file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t file_base_addr = GetBaseAddress().GetFileAddress();
+ if (file_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (file_base_addr <= file_addr)
+ return (file_addr - file_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::ContainsLoadAddress(const Address &addr,
+ Target *target) const {
+ if (addr.GetSection() == m_base_addr.GetSection())
+ return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize();
+ addr_t load_base_addr = GetBaseAddress().GetLoadAddress(target);
+ if (load_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t load_addr = addr.GetLoadAddress(target);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (load_base_addr <= load_addr)
+ return (load_addr - load_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::ContainsLoadAddress(addr_t load_addr, Target *target) const {
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ addr_t load_base_addr = GetBaseAddress().GetLoadAddress(target);
+ if (load_base_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (load_base_addr <= load_addr)
+ return (load_addr - load_base_addr) < GetByteSize();
+
+ return false;
+}
+
+bool AddressRange::Extend(const AddressRange &rhs_range) {
+ addr_t lhs_end_addr = GetBaseAddress().GetFileAddress() + GetByteSize();
+ addr_t rhs_base_addr = rhs_range.GetBaseAddress().GetFileAddress();
+
+ if (!ContainsFileAddress(rhs_range.GetBaseAddress()) &&
+ lhs_end_addr != rhs_base_addr)
+ // The ranges don't intersect at all on the right side of this range.
+ return false;
+
+ addr_t rhs_end_addr = rhs_base_addr + rhs_range.GetByteSize();
+ if (lhs_end_addr >= rhs_end_addr)
+ // The rhs range totally overlaps this one, nothing to add.
+ return false;
+
+ m_byte_size += rhs_end_addr - lhs_end_addr;
+ return true;
+}
+
+void AddressRange::Clear() {
+ m_base_addr.Clear();
+ m_byte_size = 0;
+}
+
+bool AddressRange::Dump(Stream *s, Target *target, Address::DumpStyle style,
+ Address::DumpStyle fallback_style) const {
+ addr_t vmaddr = LLDB_INVALID_ADDRESS;
+ int addr_size = sizeof(addr_t);
+ if (target)
+ addr_size = target->GetArchitecture().GetAddressByteSize();
+
+ bool show_module = false;
+ switch (style) {
+ default:
+ break;
+ case Address::DumpStyleSectionNameOffset:
+ case Address::DumpStyleSectionPointerOffset:
+ s->PutChar('[');
+ m_base_addr.Dump(s, target, style, fallback_style);
+ s->PutChar('-');
+ s->Address(m_base_addr.GetOffset() + GetByteSize(), addr_size);
+ s->PutChar(')');
+ return true;
+ break;
+
+ case Address::DumpStyleModuleWithFileAddress:
+ show_module = true;
+ LLVM_FALLTHROUGH;
+ case Address::DumpStyleFileAddress:
+ vmaddr = m_base_addr.GetFileAddress();
+ break;
+
+ case Address::DumpStyleLoadAddress:
+ vmaddr = m_base_addr.GetLoadAddress(target);
+ break;
+ }
+
+ if (vmaddr != LLDB_INVALID_ADDRESS) {
+ if (show_module) {
+ ModuleSP module_sp(GetBaseAddress().GetModule());
+ if (module_sp)
+ s->Printf("%s", module_sp->GetFileSpec().GetFilename().AsCString(
+ "<Unknown>"));
+ }
+ s->AddressRange(vmaddr, vmaddr + GetByteSize(), addr_size);
+ return true;
+ } else if (fallback_style != Address::DumpStyleInvalid) {
+ return Dump(s, target, fallback_style, Address::DumpStyleInvalid);
+ }
+
+ return false;
+}
+
+void AddressRange::DumpDebug(Stream *s) const {
+ s->Printf("%p: AddressRange section = %p, offset = 0x%16.16" PRIx64
+ ", byte_size = 0x%16.16" PRIx64 "\n",
+ static_cast<const void *>(this),
+ static_cast<void *>(m_base_addr.GetSection().get()),
+ m_base_addr.GetOffset(), GetByteSize());
+}
+//
+// bool
+// lldb::operator== (const AddressRange& lhs, const AddressRange& rhs)
+//{
+// if (lhs.GetBaseAddress() == rhs.GetBaseAddress())
+// return lhs.GetByteSize() == rhs.GetByteSize();
+// return false;
+//}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressResolver.cpp b/contrib/llvm-project/lldb/source/Core/AddressResolver.cpp
new file mode 100644
index 000000000000..974d99b62065
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressResolver.cpp
@@ -0,0 +1,43 @@
+//===-- AddressResolver.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressResolver.h"
+
+#include "lldb/Core/SearchFilter.h"
+
+namespace lldb_private {
+class ModuleList;
+}
+
+using namespace lldb_private;
+
+// AddressResolver:
+AddressResolver::AddressResolver() {}
+
+AddressResolver::~AddressResolver() {}
+
+void AddressResolver::ResolveAddressInModules(SearchFilter &filter,
+ ModuleList &modules) {
+ filter.SearchInModuleList(*this, modules);
+}
+
+void AddressResolver::ResolveAddress(SearchFilter &filter) {
+ filter.Search(*this);
+}
+
+std::vector<AddressRange> &AddressResolver::GetAddressRanges() {
+ return m_address_ranges;
+}
+
+size_t AddressResolver::GetNumberOfAddresses() {
+ return m_address_ranges.size();
+}
+
+AddressRange &AddressResolver::GetAddressRangeAtIndex(size_t idx) {
+ return m_address_ranges[idx];
+}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp b/contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp
new file mode 100644
index 000000000000..4a14260c6c72
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressResolverFileLine.cpp
@@ -0,0 +1,84 @@
+//===-- AddressResolverFileLine.cpp -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressResolverFileLine.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#include <inttypes.h>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// AddressResolverFileLine:
+AddressResolverFileLine::AddressResolverFileLine(const FileSpec &file_spec,
+ uint32_t line_no,
+ bool check_inlines)
+ : AddressResolver(), m_file_spec(file_spec), m_line_number(line_no),
+ m_inlines(check_inlines) {}
+
+AddressResolverFileLine::~AddressResolverFileLine() {}
+
+Searcher::CallbackReturn
+AddressResolverFileLine::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr) {
+ SymbolContextList sc_list;
+ uint32_t sc_list_size;
+ CompileUnit *cu = context.comp_unit;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ sc_list_size =
+ cu->ResolveSymbolContext(m_file_spec, m_line_number, m_inlines, false,
+ eSymbolContextEverything, sc_list);
+ for (uint32_t i = 0; i < sc_list_size; i++) {
+ SymbolContext sc;
+ if (sc_list.GetContextAtIndex(i, sc)) {
+ Address line_start = sc.line_entry.range.GetBaseAddress();
+ addr_t byte_size = sc.line_entry.range.GetByteSize();
+ if (line_start.IsValid()) {
+ AddressRange new_range(line_start, byte_size);
+ m_address_ranges.push_back(new_range);
+ if (log) {
+ StreamString s;
+ // new_bp_loc->GetDescription (&s, lldb::eDescriptionLevelVerbose);
+ // LLDB_LOGF(log, "Added address: %s\n", s.GetData());
+ }
+ } else {
+ LLDB_LOGF(log,
+ "error: Unable to resolve address at file address 0x%" PRIx64
+ " for %s:%d\n",
+ line_start.GetFileAddress(),
+ m_file_spec.GetFilename().AsCString("<Unknown>"),
+ m_line_number);
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth AddressResolverFileLine::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+void AddressResolverFileLine::GetDescription(Stream *s) {
+ s->Printf("File and line address - file: \"%s\" line: %u",
+ m_file_spec.GetFilename().AsCString("<Unknown>"), m_line_number);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp b/contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp
new file mode 100644
index 000000000000..6b9b7b2de723
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/AddressResolverName.cpp
@@ -0,0 +1,198 @@
+//===-- AddressResolverName.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/AddressResolverName.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+AddressResolverName::AddressResolverName(const char *func_name,
+ AddressResolver::MatchType type)
+ : AddressResolver(), m_func_name(func_name), m_class_name(nullptr),
+ m_regex(), m_match_type(type) {
+ if (m_match_type == AddressResolver::Regexp) {
+ m_regex = RegularExpression(m_func_name.GetStringRef());
+ if (!m_regex.IsValid()) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ if (log)
+ log->Warning("function name regexp: \"%s\" did not compile.",
+ m_func_name.AsCString());
+ }
+ }
+}
+
+AddressResolverName::AddressResolverName(RegularExpression func_regex)
+ : AddressResolver(), m_func_name(nullptr), m_class_name(nullptr),
+ m_regex(std::move(func_regex)), m_match_type(AddressResolver::Regexp) {}
+
+AddressResolverName::AddressResolverName(const char *class_name,
+ const char *method,
+ AddressResolver::MatchType type)
+ : AddressResolver(), m_func_name(method), m_class_name(class_name),
+ m_regex(), m_match_type(type) {}
+
+AddressResolverName::~AddressResolverName() = default;
+
+// FIXME: Right now we look at the module level, and call the module's
+// "FindFunctions".
+// Greg says he will add function tables, maybe at the CompileUnit level to
+// accelerate function lookup. At that point, we should switch the depth to
+// CompileUnit, and look in these tables.
+
+Searcher::CallbackReturn
+AddressResolverName::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr) {
+ SymbolContextList func_list;
+ SymbolContextList sym_list;
+
+ bool skip_prologue = true;
+ uint32_t i;
+ SymbolContext sc;
+ Address func_addr;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
+
+ if (m_class_name) {
+ if (log)
+ log->Warning("Class/method function specification not supported yet.\n");
+ return Searcher::eCallbackReturnStop;
+ }
+
+ const bool include_symbols = false;
+ const bool include_inlines = true;
+ switch (m_match_type) {
+ case AddressResolver::Exact:
+ if (context.module_sp) {
+ context.module_sp->FindSymbolsWithNameAndType(m_func_name,
+ eSymbolTypeCode, sym_list);
+ context.module_sp->FindFunctions(m_func_name, nullptr,
+ eFunctionNameTypeAuto, include_symbols,
+ include_inlines, func_list);
+ }
+ break;
+
+ case AddressResolver::Regexp:
+ if (context.module_sp) {
+ context.module_sp->FindSymbolsMatchingRegExAndType(
+ m_regex, eSymbolTypeCode, sym_list);
+ context.module_sp->FindFunctions(m_regex, include_symbols,
+ include_inlines, func_list);
+ }
+ break;
+
+ case AddressResolver::Glob:
+ if (log)
+ log->Warning("glob is not supported yet.");
+ break;
+ }
+
+ // Remove any duplicates between the function list and the symbol list
+ if (func_list.GetSize()) {
+ for (i = 0; i < func_list.GetSize(); i++) {
+ if (!func_list.GetContextAtIndex(i, sc))
+ continue;
+
+ if (sc.function == nullptr)
+ continue;
+ uint32_t j = 0;
+ while (j < sym_list.GetSize()) {
+ SymbolContext symbol_sc;
+ if (sym_list.GetContextAtIndex(j, symbol_sc)) {
+ if (symbol_sc.symbol && symbol_sc.symbol->ValueIsAddress()) {
+ if (sc.function->GetAddressRange().GetBaseAddress() ==
+ symbol_sc.symbol->GetAddressRef()) {
+ sym_list.RemoveContextAtIndex(j);
+ continue; // Don't increment j
+ }
+ }
+ }
+
+ j++;
+ }
+ }
+
+ for (i = 0; i < func_list.GetSize(); i++) {
+ if (func_list.GetContextAtIndex(i, sc)) {
+ if (sc.function) {
+ func_addr = sc.function->GetAddressRange().GetBaseAddress();
+ addr_t byte_size = sc.function->GetAddressRange().GetByteSize();
+ if (skip_prologue) {
+ const uint32_t prologue_byte_size =
+ sc.function->GetPrologueByteSize();
+ if (prologue_byte_size) {
+ func_addr.SetOffset(func_addr.GetOffset() + prologue_byte_size);
+ byte_size -= prologue_byte_size;
+ }
+ }
+
+ if (filter.AddressPasses(func_addr)) {
+ AddressRange new_range(func_addr, byte_size);
+ m_address_ranges.push_back(new_range);
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < sym_list.GetSize(); i++) {
+ if (sym_list.GetContextAtIndex(i, sc)) {
+ if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ func_addr = sc.symbol->GetAddressRef();
+ addr_t byte_size = sc.symbol->GetByteSize();
+
+ if (skip_prologue) {
+ const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize();
+ if (prologue_byte_size) {
+ func_addr.SetOffset(func_addr.GetOffset() + prologue_byte_size);
+ byte_size -= prologue_byte_size;
+ }
+ }
+
+ if (filter.AddressPasses(func_addr)) {
+ AddressRange new_range(func_addr, byte_size);
+ m_address_ranges.push_back(new_range);
+ }
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth AddressResolverName::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+void AddressResolverName::GetDescription(Stream *s) {
+ s->PutCString("Address by function name: ");
+
+ if (m_match_type == AddressResolver::Regexp)
+ s->Printf("'%s' (regular expression)", m_regex.GetText().str().c_str());
+ else
+ s->Printf("'%s'", m_func_name.AsCString());
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Communication.cpp b/contrib/llvm-project/lldb/source/Core/Communication.cpp
new file mode 100644
index 000000000000..0afd897a2093
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Communication.cpp
@@ -0,0 +1,431 @@
+//===-- Communication.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Communication.h"
+
+#include "lldb/Host/HostThread.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/Connection.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Compiler.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cstring>
+#include <memory>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+ConstString &Communication::GetStaticBroadcasterClass() {
+ static ConstString class_name("lldb.communication");
+ return class_name;
+}
+
+Communication::Communication(const char *name)
+ : Broadcaster(nullptr, name), m_connection_sp(),
+ m_read_thread_enabled(false), m_read_thread_did_exit(false), m_bytes(),
+ m_bytes_mutex(), m_write_mutex(), m_synchronize_mutex(),
+ m_callback(nullptr), m_callback_baton(nullptr), m_close_on_eof(true)
+
+{
+
+ LLDB_LOG(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::Communication (name = {1})", this, name);
+
+ SetEventName(eBroadcastBitDisconnected, "disconnected");
+ SetEventName(eBroadcastBitReadThreadGotBytes, "got bytes");
+ SetEventName(eBroadcastBitReadThreadDidExit, "read thread did exit");
+ SetEventName(eBroadcastBitReadThreadShouldExit, "read thread should exit");
+ SetEventName(eBroadcastBitPacketAvailable, "packet available");
+ SetEventName(eBroadcastBitNoMorePendingInput, "no more pending input");
+
+ CheckInWithManager();
+}
+
+Communication::~Communication() {
+ LLDB_LOG(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::~Communication (name = {1})", this,
+ GetBroadcasterName().AsCString());
+ Clear();
+}
+
+void Communication::Clear() {
+ SetReadThreadBytesReceivedCallback(nullptr, nullptr);
+ Disconnect(nullptr);
+ StopReadThread(nullptr);
+}
+
+ConnectionStatus Communication::Connect(const char *url, Status *error_ptr) {
+ Clear();
+
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::Connect (url = {1})", this, url);
+
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ if (connection_sp)
+ return connection_sp->Connect(url, error_ptr);
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ return eConnectionStatusNoConnection;
+}
+
+ConnectionStatus Communication::Disconnect(Status *error_ptr) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::Disconnect ()", this);
+
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ if (connection_sp) {
+ ConnectionStatus status = connection_sp->Disconnect(error_ptr);
+ // We currently don't protect connection_sp with any mutex for multi-
+ // threaded environments. So lets not nuke our connection class without
+ // putting some multi-threaded protections in. We also probably don't want
+ // to pay for the overhead it might cause if every time we access the
+ // connection we have to take a lock.
+ //
+ // This unique pointer will cleanup after itself when this object goes
+ // away, so there is no need to currently have it destroy itself
+ // immediately upon disconnect.
+ // connection_sp.reset();
+ return status;
+ }
+ return eConnectionStatusNoConnection;
+}
+
+bool Communication::IsConnected() const {
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ return (connection_sp ? connection_sp->IsConnected() : false);
+}
+
+bool Communication::HasConnection() const {
+ return m_connection_sp.get() != nullptr;
+}
+
+size_t Communication::Read(void *dst, size_t dst_len,
+ const Timeout<std::micro> &timeout,
+ ConnectionStatus &status, Status *error_ptr) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION);
+ LLDB_LOG(
+ log,
+ "this = {0}, dst = {1}, dst_len = {2}, timeout = {3}, connection = {4}",
+ this, dst, dst_len, timeout, m_connection_sp.get());
+
+ if (m_read_thread_enabled) {
+ // We have a dedicated read thread that is getting data for us
+ size_t cached_bytes = GetCachedBytes(dst, dst_len);
+ if (cached_bytes > 0 || (timeout && timeout->count() == 0)) {
+ status = eConnectionStatusSuccess;
+ return cached_bytes;
+ }
+
+ if (!m_connection_sp) {
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ status = eConnectionStatusNoConnection;
+ return 0;
+ }
+
+ ListenerSP listener_sp(Listener::MakeListener("Communication::Read"));
+ listener_sp->StartListeningForEvents(
+ this, eBroadcastBitReadThreadGotBytes | eBroadcastBitReadThreadDidExit);
+ EventSP event_sp;
+ while (listener_sp->GetEvent(event_sp, timeout)) {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_type & eBroadcastBitReadThreadGotBytes) {
+ return GetCachedBytes(dst, dst_len);
+ }
+
+ if (event_type & eBroadcastBitReadThreadDidExit) {
+ if (GetCloseOnEOF())
+ Disconnect(nullptr);
+ break;
+ }
+ }
+ return 0;
+ }
+
+ // We aren't using a read thread, just read the data synchronously in this
+ // thread.
+ return ReadFromConnection(dst, dst_len, timeout, status, error_ptr);
+}
+
+size_t Communication::Write(const void *src, size_t src_len,
+ ConnectionStatus &status, Status *error_ptr) {
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+
+ std::lock_guard<std::mutex> guard(m_write_mutex);
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::Write (src = {1}, src_len = %" PRIu64
+ ") connection = {2}",
+ this, src, (uint64_t)src_len, connection_sp.get());
+
+ if (connection_sp)
+ return connection_sp->Write(src, src_len, status, error_ptr);
+
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ status = eConnectionStatusNoConnection;
+ return 0;
+}
+
+bool Communication::StartReadThread(Status *error_ptr) {
+ if (error_ptr)
+ error_ptr->Clear();
+
+ if (m_read_thread.IsJoinable())
+ return true;
+
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::StartReadThread ()", this);
+
+ char thread_name[1024];
+ snprintf(thread_name, sizeof(thread_name), "<lldb.comm.%s>",
+ GetBroadcasterName().AsCString());
+
+ m_read_thread_enabled = true;
+ m_read_thread_did_exit = false;
+ auto maybe_thread = ThreadLauncher::LaunchThread(
+ thread_name, Communication::ReadThread, this);
+ if (maybe_thread) {
+ m_read_thread = *maybe_thread;
+ } else {
+ if (error_ptr)
+ *error_ptr = Status(maybe_thread.takeError());
+ else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(maybe_thread.takeError()));
+ }
+ }
+
+ if (!m_read_thread.IsJoinable())
+ m_read_thread_enabled = false;
+
+ return m_read_thread_enabled;
+}
+
+bool Communication::StopReadThread(Status *error_ptr) {
+ if (!m_read_thread.IsJoinable())
+ return true;
+
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::StopReadThread ()", this);
+
+ m_read_thread_enabled = false;
+
+ BroadcastEvent(eBroadcastBitReadThreadShouldExit, nullptr);
+
+ // error = m_read_thread.Cancel();
+
+ Status error = m_read_thread.Join(nullptr);
+ return error.Success();
+}
+
+bool Communication::JoinReadThread(Status *error_ptr) {
+ if (!m_read_thread.IsJoinable())
+ return true;
+
+ Status error = m_read_thread.Join(nullptr);
+ return error.Success();
+}
+
+size_t Communication::GetCachedBytes(void *dst, size_t dst_len) {
+ std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex);
+ if (!m_bytes.empty()) {
+ // If DST is nullptr and we have a thread, then return the number of bytes
+ // that are available so the caller can call again
+ if (dst == nullptr)
+ return m_bytes.size();
+
+ const size_t len = std::min<size_t>(dst_len, m_bytes.size());
+
+ ::memcpy(dst, m_bytes.c_str(), len);
+ m_bytes.erase(m_bytes.begin(), m_bytes.begin() + len);
+
+ return len;
+ }
+ return 0;
+}
+
+void Communication::AppendBytesToCache(const uint8_t *bytes, size_t len,
+ bool broadcast,
+ ConnectionStatus status) {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION),
+ "{0} Communication::AppendBytesToCache (src = {1}, src_len = {2}, "
+ "broadcast = {3})",
+ this, bytes, (uint64_t)len, broadcast);
+ if ((bytes == nullptr || len == 0) &&
+ (status != lldb::eConnectionStatusEndOfFile))
+ return;
+ if (m_callback) {
+ // If the user registered a callback, then call it and do not broadcast
+ m_callback(m_callback_baton, bytes, len);
+ } else if (bytes != nullptr && len > 0) {
+ std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex);
+ m_bytes.append((const char *)bytes, len);
+ if (broadcast)
+ BroadcastEventIfUnique(eBroadcastBitReadThreadGotBytes);
+ }
+}
+
+size_t Communication::ReadFromConnection(void *dst, size_t dst_len,
+ const Timeout<std::micro> &timeout,
+ ConnectionStatus &status,
+ Status *error_ptr) {
+ lldb::ConnectionSP connection_sp(m_connection_sp);
+ if (connection_sp)
+ return connection_sp->Read(dst, dst_len, timeout, status, error_ptr);
+
+ if (error_ptr)
+ error_ptr->SetErrorString("Invalid connection.");
+ status = eConnectionStatusNoConnection;
+ return 0;
+}
+
+bool Communication::ReadThreadIsRunning() { return m_read_thread_enabled; }
+
+lldb::thread_result_t Communication::ReadThread(lldb::thread_arg_t p) {
+ Communication *comm = (Communication *)p;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION));
+
+ LLDB_LOGF(log, "%p Communication::ReadThread () thread starting...", p);
+
+ uint8_t buf[1024];
+
+ Status error;
+ ConnectionStatus status = eConnectionStatusSuccess;
+ bool done = false;
+ while (!done && comm->m_read_thread_enabled) {
+ size_t bytes_read = comm->ReadFromConnection(
+ buf, sizeof(buf), std::chrono::seconds(5), status, &error);
+ if (bytes_read > 0)
+ comm->AppendBytesToCache(buf, bytes_read, true, status);
+ else if ((bytes_read == 0) && status == eConnectionStatusEndOfFile) {
+ if (comm->GetCloseOnEOF())
+ comm->Disconnect();
+ comm->AppendBytesToCache(buf, bytes_read, true, status);
+ }
+
+ switch (status) {
+ case eConnectionStatusSuccess:
+ break;
+
+ case eConnectionStatusEndOfFile:
+ done = true;
+ break;
+ case eConnectionStatusError: // Check GetError() for details
+ if (error.GetType() == eErrorTypePOSIX && error.GetError() == EIO) {
+ // EIO on a pipe is usually caused by remote shutdown
+ comm->Disconnect();
+ done = true;
+ }
+ if (error.Fail())
+ LLDB_LOG(log, "error: {0}, status = {1}", error,
+ Communication::ConnectionStatusAsCString(status));
+ break;
+ case eConnectionStatusInterrupted: // Synchronization signal from
+ // SynchronizeWithReadThread()
+ // The connection returns eConnectionStatusInterrupted only when there is
+ // no input pending to be read, so we can signal that.
+ comm->BroadcastEvent(eBroadcastBitNoMorePendingInput);
+ break;
+ case eConnectionStatusNoConnection: // No connection
+ case eConnectionStatusLostConnection: // Lost connection while connected to
+ // a valid connection
+ done = true;
+ LLVM_FALLTHROUGH;
+ case eConnectionStatusTimedOut: // Request timed out
+ if (error.Fail())
+ LLDB_LOG(log, "error: {0}, status = {1}", error,
+ Communication::ConnectionStatusAsCString(status));
+ break;
+ }
+ }
+ log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMUNICATION);
+ if (log)
+ LLDB_LOGF(log, "%p Communication::ReadThread () thread exiting...", p);
+
+ comm->m_read_thread_did_exit = true;
+ // Let clients know that this thread is exiting
+ comm->BroadcastEvent(eBroadcastBitNoMorePendingInput);
+ comm->BroadcastEvent(eBroadcastBitReadThreadDidExit);
+ return {};
+}
+
+void Communication::SetReadThreadBytesReceivedCallback(
+ ReadThreadBytesReceived callback, void *callback_baton) {
+ m_callback = callback;
+ m_callback_baton = callback_baton;
+}
+
+void Communication::SynchronizeWithReadThread() {
+ // Only one thread can do the synchronization dance at a time.
+ std::lock_guard<std::mutex> guard(m_synchronize_mutex);
+
+ // First start listening for the synchronization event.
+ ListenerSP listener_sp(
+ Listener::MakeListener("Communication::SyncronizeWithReadThread"));
+ listener_sp->StartListeningForEvents(this, eBroadcastBitNoMorePendingInput);
+
+ // If the thread is not running, there is no point in synchronizing.
+ if (!m_read_thread_enabled || m_read_thread_did_exit)
+ return;
+
+ // Notify the read thread.
+ m_connection_sp->InterruptRead();
+
+ // Wait for the synchronization event.
+ EventSP event_sp;
+ listener_sp->GetEvent(event_sp, llvm::None);
+}
+
+void Communication::SetConnection(Connection *connection) {
+ Disconnect(nullptr);
+ StopReadThread(nullptr);
+ m_connection_sp.reset(connection);
+}
+
+const char *
+Communication::ConnectionStatusAsCString(lldb::ConnectionStatus status) {
+ switch (status) {
+ case eConnectionStatusSuccess:
+ return "success";
+ case eConnectionStatusError:
+ return "error";
+ case eConnectionStatusTimedOut:
+ return "timed out";
+ case eConnectionStatusNoConnection:
+ return "no connection";
+ case eConnectionStatusLostConnection:
+ return "lost connection";
+ case eConnectionStatusEndOfFile:
+ return "end of file";
+ case eConnectionStatusInterrupted:
+ return "interrupted";
+ }
+
+ static char unknown_state_string[64];
+ snprintf(unknown_state_string, sizeof(unknown_state_string),
+ "ConnectionStatus = %i", status);
+ return unknown_state_string;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/CoreProperties.td b/contrib/llvm-project/lldb/source/Core/CoreProperties.td
new file mode 100644
index 000000000000..014927c65c6f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/CoreProperties.td
@@ -0,0 +1,118 @@
+include "../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "modulelist" in {
+ def EnableExternalLookup: Property<"enable-external-lookup", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"Control the use of external tools and repositories to locate symbol files. Directories listed in target.debug-file-search-paths and directory of the executable are always checked first for separate debug info files. Then depending on this setting: On macOS, Spotlight would be also used to locate a matching .dSYM bundle based on the UUID of the executable. On NetBSD, directory /usr/libdata/debug would be also searched. On platforms other than NetBSD directory /usr/lib/debug would be also searched.">;
+ def ClangModulesCachePath: Property<"clang-modules-cache-path", "FileSpec">,
+ Global,
+ DefaultStringValue<"">,
+ Desc<"The path to the clang modules cache directory (-fmodules-cache-path).">;
+}
+
+let Definition = "debugger" in {
+ def AutoConfirm: Property<"auto-confirm", "Boolean">,
+ Global,
+ DefaultFalse,
+ Desc<"If true all confirmation prompts will receive their default reply.">;
+ def DisassemblyFormat: Property<"disassembly-format", "FormatEntity">,
+ Global,
+ DefaultStringValue<"{${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\\\\n}{${function.changed}\\\\n{${module.file.basename}`}{${function.name-without-args}}:\\\\n}{${current-pc-arrow} }${addr-file-or-load}{ <${function.concrete-only-addr-offset-no-padding}>}: ">,
+ Desc<"The default disassembly format string to use when disassembling instruction sequences.">;
+ def FrameFormat: Property<"frame-format", "FormatEntity">,
+ Global,
+ DefaultStringValue<"frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">,
+ Desc<"The default frame format string to use when displaying stack frame information for threads.">;
+ def NotiftVoid: Property<"notify-void", "Boolean">,
+ Global,
+ DefaultFalse,
+ Desc<"Notify the user explicitly if an expression returns void (default: false).">;
+ def Prompt: Property<"prompt", "String">,
+ Global,
+ DefaultEnumValue<"OptionValueString::eOptionEncodeCharacterEscapeSequences">,
+ DefaultStringValue<"(lldb) ">,
+ Desc<"The debugger command line prompt displayed for the user.">;
+ def ScriptLanguage: Property<"script-lang", "Enum">,
+ Global,
+ DefaultEnumValue<"eScriptLanguagePython">,
+ EnumValues<"OptionEnumValues(g_language_enumerators)">,
+ Desc<"The script language to be used for evaluating user-written scripts.">;
+ def StopDisassemblyCount: Property<"stop-disassembly-count", "SInt64">,
+ Global,
+ DefaultUnsignedValue<4>,
+ Desc<"The number of disassembly lines to show when displaying a stopped context.">;
+ def StopDisassemblyDisplay: Property<"stop-disassembly-display", "Enum">,
+ Global,
+ DefaultEnumValue<"Debugger::eStopDisassemblyTypeNoDebugInfo">,
+ EnumValues<"OptionEnumValues(g_show_disassembly_enum_values)">,
+ Desc<"Control when to display disassembly when displaying a stopped context.">;
+ def StopLineCountAfter: Property<"stop-line-count-after", "SInt64">,
+ Global,
+ DefaultUnsignedValue<3>,
+ Desc<"The number of sources lines to display that come after the current source line when displaying a stopped context.">;
+ def StopLineCountBefore: Property<"stop-line-count-before", "SInt64">,
+ Global,
+ DefaultUnsignedValue<3>,
+ Desc<"The number of sources lines to display that come before the current source line when displaying a stopped context.">;
+ def HighlightSource: Property<"highlight-source", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"If true, LLDB will highlight the displayed source code.">;
+ def StopShowColumn: Property<"stop-show-column", "Enum">,
+ DefaultEnumValue<"eStopShowColumnAnsiOrCaret">,
+ EnumValues<"OptionEnumValues(s_stop_show_column_values)">,
+ Desc<"If true, LLDB will use the column information from the debug info to mark the current position when displaying a stopped context.">;
+ def StopShowColumnAnsiPrefix: Property<"stop-show-column-ansi-prefix", "String">,
+ Global,
+ DefaultStringValue<"${ansi.underline}">,
+ Desc<"When displaying the column marker in a color-enabled (i.e. ANSI) terminal, use the ANSI terminal code specified in this format at the immediately before the column to be marked.">;
+ def StopShowColumnAnsiSuffix: Property<"stop-show-column-ansi-suffix", "String">,
+ Global,
+ DefaultStringValue<"${ansi.normal}">,
+ Desc<"When displaying the column marker in a color-enabled (i.e. ANSI) terminal, use the ANSI terminal code specified in this format immediately after the column to be marked.">;
+ def TerminalWidth: Property<"term-width", "SInt64">,
+ Global,
+ DefaultUnsignedValue<80>,
+ Desc<"The maximum number of columns to use for displaying text.">;
+ def ThreadFormat: Property<"thread-format", "FormatEntity">,
+ Global,
+ DefaultStringValue<"thread #${thread.index}: tid = ${thread.id%tid}{, ${frame.pc}}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{, name = ${ansi.fg.green}'${thread.name}'${ansi.normal}}{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}{, activity = ${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}{, ${thread.info.trace_messages} messages}{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}{\\\\nReturn value: ${thread.return-value}}{\\\\nCompleted expression: ${thread.completed-expression}}\\\\n">,
+ Desc<"The default thread format string to use when displaying thread information.">;
+ def ThreadStopFormat: Property<"thread-stop-format", "FormatEntity">,
+ Global,
+ DefaultStringValue<"thread #${thread.index}{, name = '${thread.name}'}{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}{, activity = ${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}{, ${thread.info.trace_messages} messages}{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}{\\\\nReturn value: ${thread.return-value}}{\\\\nCompleted expression: ${thread.completed-expression}}\\\\n">,
+ Desc<"The default thread format string to use when displaying thread information as part of the stop display.">;
+ def UseExternalEditor: Property<"use-external-editor", "Boolean">,
+ Global,
+ DefaultFalse,
+ Desc<"Whether to use an external editor or not.">;
+ def UseColor: Property<"use-color", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"Whether to use Ansi color codes or not.">;
+ def AutoOneLineSummaries: Property<"auto-one-line-summaries", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"If true, LLDB will automatically display small structs in one-liner format (default: true).">;
+ def AutoIndent: Property<"auto-indent", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"If true, LLDB will auto indent/outdent code. Currently only supported in the REPL (default: true).">;
+ def PrintDecls: Property<"print-decls", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"If true, LLDB will print the values of variables declared in an expression. Currently only supported in the REPL (default: true).">;
+ def TabSize: Property<"tab-size", "UInt64">,
+ Global,
+ DefaultUnsignedValue<4>,
+ Desc<"The tab size to use when indenting code in multi-line input mode (default: 4).">;
+ def EscapeNonPrintables: Property<"escape-non-printables", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">;
+ def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">,
+ Global,
+ DefaultStringValue<"frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">,
+ Desc<"The default frame format string to use when displaying stack frameinformation for threads from thread backtrace unique.">;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Debugger.cpp b/contrib/llvm-project/lldb/source/Core/Debugger.cpp
new file mode 100644
index 000000000000..18397d00dcaa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Debugger.cpp
@@ -0,0 +1,1629 @@
+//===-- Debugger.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Debugger.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamAsynchronousIO.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/Expression/REPL.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/Terminal.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/OptionValueSInt64.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StructuredDataPlugin.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamCallback.h"
+#include "lldb/Utility/StreamString.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <system_error>
+
+namespace lldb_private {
+class Address;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static lldb::user_id_t g_unique_id = 1;
+static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024;
+
+#pragma mark Static Functions
+
+typedef std::vector<DebuggerSP> DebuggerList;
+static std::recursive_mutex *g_debugger_list_mutex_ptr =
+ nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
+static DebuggerList *g_debugger_list_ptr =
+ nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
+
+static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = {
+ {
+ Debugger::eStopDisassemblyTypeNever,
+ "never",
+ "Never show disassembly when displaying a stop context.",
+ },
+ {
+ Debugger::eStopDisassemblyTypeNoDebugInfo,
+ "no-debuginfo",
+ "Show disassembly when there is no debug information.",
+ },
+ {
+ Debugger::eStopDisassemblyTypeNoSource,
+ "no-source",
+ "Show disassembly when there is no source information, or the source "
+ "file "
+ "is missing when displaying a stop context.",
+ },
+ {
+ Debugger::eStopDisassemblyTypeAlways,
+ "always",
+ "Always show disassembly when displaying a stop context.",
+ },
+};
+
+static constexpr OptionEnumValueElement g_language_enumerators[] = {
+ {
+ eScriptLanguageNone,
+ "none",
+ "Disable scripting languages.",
+ },
+ {
+ eScriptLanguagePython,
+ "python",
+ "Select python as the default scripting language.",
+ },
+ {
+ eScriptLanguageDefault,
+ "default",
+ "Select the lldb default as the default scripting language.",
+ },
+};
+
+#define MODULE_WITH_FUNC \
+ "{ " \
+ "${module.file.basename}{`${function.name-with-args}" \
+ "{${frame.no-debug}${function.pc-offset}}}}"
+
+#define MODULE_WITH_FUNC_NO_ARGS \
+ "{ " \
+ "${module.file.basename}{`${function.name-without-args}" \
+ "{${frame.no-debug}${function.pc-offset}}}}"
+
+#define FILE_AND_LINE \
+ "{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}" \
+ ":${ansi.fg.yellow}${line.number}${ansi.normal}" \
+ "{:${ansi.fg.yellow}${line.column}${ansi.normal}}}"
+
+#define IS_OPTIMIZED "{${function.is-optimized} [opt]}"
+
+#define IS_ARTIFICIAL "{${frame.is-artificial} [artificial]}"
+
+#define DEFAULT_THREAD_FORMAT \
+ "thread #${thread.index}: tid = ${thread.id%tid}" \
+ "{, ${frame.pc}}" MODULE_WITH_FUNC FILE_AND_LINE \
+ "{, name = ${ansi.fg.green}'${thread.name}'${ansi.normal}}" \
+ "{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}" \
+ "{, activity = " \
+ "${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}" \
+ "{, ${thread.info.trace_messages} messages}" \
+ "{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}" \
+ "{\\nReturn value: ${thread.return-value}}" \
+ "{\\nCompleted expression: ${thread.completed-expression}}" \
+ "\\n"
+
+#define DEFAULT_THREAD_STOP_FORMAT \
+ "thread #${thread.index}{, name = '${thread.name}'}" \
+ "{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}" \
+ "{, activity = " \
+ "${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}" \
+ "{, ${thread.info.trace_messages} messages}" \
+ "{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}" \
+ "{\\nReturn value: ${thread.return-value}}" \
+ "{\\nCompleted expression: ${thread.completed-expression}}" \
+ "\\n"
+
+#define DEFAULT_FRAME_FORMAT \
+ "frame #${frame.index}: " \
+ "${ansi.fg.yellow}${frame.pc}${ansi.normal}" MODULE_WITH_FUNC FILE_AND_LINE \
+ IS_OPTIMIZED IS_ARTIFICIAL "\\n"
+
+#define DEFAULT_FRAME_FORMAT_NO_ARGS \
+ "frame #${frame.index}: " \
+ "${ansi.fg.yellow}${frame.pc}${ansi.normal}" MODULE_WITH_FUNC_NO_ARGS \
+ FILE_AND_LINE IS_OPTIMIZED IS_ARTIFICIAL "\\n"
+
+// Three parts to this disassembly format specification:
+// 1. If this is a new function/symbol (no previous symbol/function), print
+// dylib`funcname:\n
+// 2. If this is a symbol context change (different from previous
+// symbol/function), print
+// dylib`funcname:\n
+// 3. print
+// address <+offset>:
+#define DEFAULT_DISASSEMBLY_FORMAT \
+ "{${function.initial-function}{${module.file.basename}`}{${function.name-" \
+ "without-args}}:\\n}{${function.changed}\\n{${module.file.basename}`}{${" \
+ "function.name-without-args}}:\\n}{${current-pc-arrow} " \
+ "}${addr-file-or-load}{ " \
+ "<${function.concrete-only-addr-offset-no-padding}>}: "
+
+// gdb's disassembly format can be emulated with ${current-pc-arrow}${addr-
+// file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-
+// offset-no-padding}>}:
+
+// lldb's original format for disassembly would look like this format string -
+// {${function.initial-function}{${module.file.basename}`}{${function.name-
+// without-
+// args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-
+// without-args}}:\n}{${current-pc-arrow} }{${addr-file-or-load}}:
+
+static constexpr OptionEnumValueElement s_stop_show_column_values[] = {
+ {
+ eStopShowColumnAnsiOrCaret,
+ "ansi-or-caret",
+ "Highlight the stop column with ANSI terminal codes when color/ANSI "
+ "mode is enabled; otherwise, fall back to using a text-only caret (^) "
+ "as if \"caret-only\" mode was selected.",
+ },
+ {
+ eStopShowColumnAnsi,
+ "ansi",
+ "Highlight the stop column with ANSI terminal codes when running LLDB "
+ "with color/ANSI enabled.",
+ },
+ {
+ eStopShowColumnCaret,
+ "caret",
+ "Highlight the stop column with a caret character (^) underneath the "
+ "stop column. This method introduces a new line in source listings "
+ "that display thread stop locations.",
+ },
+ {
+ eStopShowColumnNone,
+ "none",
+ "Do not highlight the stop column.",
+ },
+};
+
+#define LLDB_PROPERTIES_debugger
+#include "CoreProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_debugger
+#include "CorePropertiesEnum.inc"
+};
+
+LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr;
+
+Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op,
+ llvm::StringRef property_path,
+ llvm::StringRef value) {
+ bool is_load_script =
+ (property_path == "target.load-script-from-symbol-file");
+ // These properties might change how we visualize data.
+ bool invalidate_data_vis = (property_path == "escape-non-printables");
+ invalidate_data_vis |=
+ (property_path == "target.max-zero-padding-in-float-format");
+ if (invalidate_data_vis) {
+ DataVisualization::ForceUpdate();
+ }
+
+ TargetSP target_sp;
+ LoadScriptFromSymFile load_script_old_value;
+ if (is_load_script && exe_ctx->GetTargetSP()) {
+ target_sp = exe_ctx->GetTargetSP();
+ load_script_old_value =
+ target_sp->TargetProperties::GetLoadScriptFromSymbolFile();
+ }
+ Status error(Properties::SetPropertyValue(exe_ctx, op, property_path, value));
+ if (error.Success()) {
+ // FIXME it would be nice to have "on-change" callbacks for properties
+ if (property_path == g_debugger_properties[ePropertyPrompt].name) {
+ llvm::StringRef new_prompt = GetPrompt();
+ std::string str = lldb_private::ansi::FormatAnsiTerminalCodes(
+ new_prompt, GetUseColor());
+ if (str.length())
+ new_prompt = str;
+ GetCommandInterpreter().UpdatePrompt(new_prompt);
+ auto bytes = std::make_unique<EventDataBytes>(new_prompt);
+ auto prompt_change_event_sp = std::make_shared<Event>(
+ CommandInterpreter::eBroadcastBitResetPrompt, bytes.release());
+ GetCommandInterpreter().BroadcastEvent(prompt_change_event_sp);
+ } else if (property_path == g_debugger_properties[ePropertyUseColor].name) {
+ // use-color changed. Ping the prompt so it can reset the ansi terminal
+ // codes.
+ SetPrompt(GetPrompt());
+ } else if (is_load_script && target_sp &&
+ load_script_old_value == eLoadScriptFromSymFileWarn) {
+ if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() ==
+ eLoadScriptFromSymFileTrue) {
+ std::list<Status> errors;
+ StreamString feedback_stream;
+ if (!target_sp->LoadScriptingResources(errors, &feedback_stream)) {
+ Stream &s = GetErrorStream();
+ for (auto error : errors) {
+ s.Printf("%s\n", error.AsCString());
+ }
+ if (feedback_stream.GetSize())
+ s.PutCString(feedback_stream.GetString());
+ }
+ }
+ }
+ }
+ return error;
+}
+
+bool Debugger::GetAutoConfirm() const {
+ const uint32_t idx = ePropertyAutoConfirm;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value != 0);
+}
+
+const FormatEntity::Entry *Debugger::GetDisassemblyFormat() const {
+ const uint32_t idx = ePropertyDisassemblyFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+const FormatEntity::Entry *Debugger::GetFrameFormat() const {
+ const uint32_t idx = ePropertyFrameFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+const FormatEntity::Entry *Debugger::GetFrameFormatUnique() const {
+ const uint32_t idx = ePropertyFrameFormatUnique;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+bool Debugger::GetNotifyVoid() const {
+ const uint32_t idx = ePropertyNotiftVoid;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value != 0);
+}
+
+llvm::StringRef Debugger::GetPrompt() const {
+ const uint32_t idx = ePropertyPrompt;
+ return m_collection_sp->GetPropertyAtIndexAsString(
+ nullptr, idx, g_debugger_properties[idx].default_cstr_value);
+}
+
+void Debugger::SetPrompt(llvm::StringRef p) {
+ const uint32_t idx = ePropertyPrompt;
+ m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p);
+ llvm::StringRef new_prompt = GetPrompt();
+ std::string str =
+ lldb_private::ansi::FormatAnsiTerminalCodes(new_prompt, GetUseColor());
+ if (str.length())
+ new_prompt = str;
+ GetCommandInterpreter().UpdatePrompt(new_prompt);
+}
+
+llvm::StringRef Debugger::GetReproducerPath() const {
+ auto &r = repro::Reproducer::Instance();
+ return r.GetReproducerPath().GetCString();
+}
+
+const FormatEntity::Entry *Debugger::GetThreadFormat() const {
+ const uint32_t idx = ePropertyThreadFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+const FormatEntity::Entry *Debugger::GetThreadStopFormat() const {
+ const uint32_t idx = ePropertyThreadStopFormat;
+ return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+}
+
+lldb::ScriptLanguage Debugger::GetScriptLanguage() const {
+ const uint32_t idx = ePropertyScriptLanguage;
+ return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+bool Debugger::SetScriptLanguage(lldb::ScriptLanguage script_lang) {
+ const uint32_t idx = ePropertyScriptLanguage;
+ return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx,
+ script_lang);
+}
+
+uint32_t Debugger::GetTerminalWidth() const {
+ const uint32_t idx = ePropertyTerminalWidth;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+bool Debugger::SetTerminalWidth(uint32_t term_width) {
+ const uint32_t idx = ePropertyTerminalWidth;
+ return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width);
+}
+
+bool Debugger::GetUseExternalEditor() const {
+ const uint32_t idx = ePropertyUseExternalEditor;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value != 0);
+}
+
+bool Debugger::SetUseExternalEditor(bool b) {
+ const uint32_t idx = ePropertyUseExternalEditor;
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool Debugger::GetUseColor() const {
+ const uint32_t idx = ePropertyUseColor;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value != 0);
+}
+
+bool Debugger::SetUseColor(bool b) {
+ const uint32_t idx = ePropertyUseColor;
+ bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+ SetPrompt(GetPrompt());
+ return ret;
+}
+
+bool Debugger::GetHighlightSource() const {
+ const uint32_t idx = ePropertyHighlightSource;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+StopShowColumn Debugger::GetStopShowColumn() const {
+ const uint32_t idx = ePropertyStopShowColumn;
+ return (lldb::StopShowColumn)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+llvm::StringRef Debugger::GetStopShowColumnAnsiPrefix() const {
+ const uint32_t idx = ePropertyStopShowColumnAnsiPrefix;
+ return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, "");
+}
+
+llvm::StringRef Debugger::GetStopShowColumnAnsiSuffix() const {
+ const uint32_t idx = ePropertyStopShowColumnAnsiSuffix;
+ return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, "");
+}
+
+uint32_t Debugger::GetStopSourceLineCount(bool before) const {
+ const uint32_t idx =
+ before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
+ const uint32_t idx = ePropertyStopDisassemblyDisplay;
+ return (Debugger::StopDisassemblyType)
+ m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+uint32_t Debugger::GetDisassemblyLineCount() const {
+ const uint32_t idx = ePropertyStopDisassemblyCount;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+bool Debugger::GetAutoOneLineSummaries() const {
+ const uint32_t idx = ePropertyAutoOneLineSummaries;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::GetEscapeNonPrintables() const {
+ const uint32_t idx = ePropertyEscapeNonPrintables;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::GetAutoIndent() const {
+ const uint32_t idx = ePropertyAutoIndent;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::SetAutoIndent(bool b) {
+ const uint32_t idx = ePropertyAutoIndent;
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+bool Debugger::GetPrintDecls() const {
+ const uint32_t idx = ePropertyPrintDecls;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+}
+
+bool Debugger::SetPrintDecls(bool b) {
+ const uint32_t idx = ePropertyPrintDecls;
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
+}
+
+uint32_t Debugger::GetTabSize() const {
+ const uint32_t idx = ePropertyTabSize;
+ return m_collection_sp->GetPropertyAtIndexAsUInt64(
+ nullptr, idx, g_debugger_properties[idx].default_uint_value);
+}
+
+bool Debugger::SetTabSize(uint32_t tab_size) {
+ const uint32_t idx = ePropertyTabSize;
+ return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, tab_size);
+}
+
+#pragma mark Debugger
+
+// const DebuggerPropertiesSP &
+// Debugger::GetSettings() const
+//{
+// return m_properties_sp;
+//}
+//
+
+void Debugger::Initialize(LoadPluginCallbackType load_plugin_callback) {
+ assert(g_debugger_list_ptr == nullptr &&
+ "Debugger::Initialize called more than once!");
+ g_debugger_list_mutex_ptr = new std::recursive_mutex();
+ g_debugger_list_ptr = new DebuggerList();
+ g_load_plugin_callback = load_plugin_callback;
+}
+
+void Debugger::Terminate() {
+ assert(g_debugger_list_ptr &&
+ "Debugger::Terminate called without a matching Debugger::Initialize!");
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ // Clear our master list of debugger objects
+ {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ for (const auto &debugger : *g_debugger_list_ptr)
+ debugger->Clear();
+ g_debugger_list_ptr->clear();
+ }
+ }
+}
+
+void Debugger::SettingsInitialize() { Target::SettingsInitialize(); }
+
+void Debugger::SettingsTerminate() { Target::SettingsTerminate(); }
+
+bool Debugger::LoadPlugin(const FileSpec &spec, Status &error) {
+ if (g_load_plugin_callback) {
+ llvm::sys::DynamicLibrary dynlib =
+ g_load_plugin_callback(shared_from_this(), spec, error);
+ if (dynlib.isValid()) {
+ m_loaded_plugins.push_back(dynlib);
+ return true;
+ }
+ } else {
+ // The g_load_plugin_callback is registered in SBDebugger::Initialize() and
+ // if the public API layer isn't available (code is linking against all of
+ // the internal LLDB static libraries), then we can't load plugins
+ error.SetErrorString("Public API layer is not available");
+ }
+ return false;
+}
+
+static FileSystem::EnumerateDirectoryResult
+LoadPluginCallback(void *baton, llvm::sys::fs::file_type ft,
+ llvm::StringRef path) {
+ Status error;
+
+ static ConstString g_dylibext(".dylib");
+ static ConstString g_solibext(".so");
+
+ if (!baton)
+ return FileSystem::eEnumerateDirectoryResultQuit;
+
+ Debugger *debugger = (Debugger *)baton;
+
+ namespace fs = llvm::sys::fs;
+ // If we have a regular file, a symbolic link or unknown file type, try and
+ // process the file. We must handle unknown as sometimes the directory
+ // enumeration might be enumerating a file system that doesn't have correct
+ // file type information.
+ if (ft == fs::file_type::regular_file || ft == fs::file_type::symlink_file ||
+ ft == fs::file_type::type_unknown) {
+ FileSpec plugin_file_spec(path);
+ FileSystem::Instance().Resolve(plugin_file_spec);
+
+ if (plugin_file_spec.GetFileNameExtension() != g_dylibext &&
+ plugin_file_spec.GetFileNameExtension() != g_solibext) {
+ return FileSystem::eEnumerateDirectoryResultNext;
+ }
+
+ Status plugin_load_error;
+ debugger->LoadPlugin(plugin_file_spec, plugin_load_error);
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+ } else if (ft == fs::file_type::directory_file ||
+ ft == fs::file_type::symlink_file ||
+ ft == fs::file_type::type_unknown) {
+ // Try and recurse into anything that a directory or symbolic link. We must
+ // also do this for unknown as sometimes the directory enumeration might be
+ // enumerating a file system that doesn't have correct file type
+ // information.
+ return FileSystem::eEnumerateDirectoryResultEnter;
+ }
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+}
+
+void Debugger::InstanceInitialize() {
+ const bool find_directories = true;
+ const bool find_files = true;
+ const bool find_other = true;
+ char dir_path[PATH_MAX];
+ if (FileSpec dir_spec = HostInfo::GetSystemPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, this);
+ }
+ }
+
+ if (FileSpec dir_spec = HostInfo::GetUserPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, this);
+ }
+ }
+
+ PluginManager::DebuggerInitialize(*this);
+}
+
+DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
+ void *baton) {
+ DebuggerSP debugger_sp(new Debugger(log_callback, baton));
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ g_debugger_list_ptr->push_back(debugger_sp);
+ }
+ debugger_sp->InstanceInitialize();
+ return debugger_sp;
+}
+
+void Debugger::Destroy(DebuggerSP &debugger_sp) {
+ if (!debugger_sp)
+ return;
+
+ debugger_sp->Clear();
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ if ((*pos).get() == debugger_sp.get()) {
+ g_debugger_list_ptr->erase(pos);
+ return;
+ }
+ }
+ }
+}
+
+DebuggerSP Debugger::FindDebuggerWithInstanceName(ConstString instance_name) {
+ DebuggerSP debugger_sp;
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ if ((*pos)->m_instance_name == instance_name) {
+ debugger_sp = *pos;
+ break;
+ }
+ }
+ }
+ return debugger_sp;
+}
+
+TargetSP Debugger::FindTargetWithProcessID(lldb::pid_t pid) {
+ TargetSP target_sp;
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ target_sp = (*pos)->GetTargetList().FindTargetWithProcessID(pid);
+ if (target_sp)
+ break;
+ }
+ }
+ return target_sp;
+}
+
+TargetSP Debugger::FindTargetWithProcess(Process *process) {
+ TargetSP target_sp;
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ target_sp = (*pos)->GetTargetList().FindTargetWithProcess(process);
+ if (target_sp)
+ break;
+ }
+ }
+ return target_sp;
+}
+
+Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
+ : UserID(g_unique_id++),
+ Properties(std::make_shared<OptionValueProperties>()),
+ m_input_file_sp(std::make_shared<NativeFile>(stdin, false)),
+ m_output_stream_sp(std::make_shared<StreamFile>(stdout, false)),
+ m_error_stream_sp(std::make_shared<StreamFile>(stderr, false)),
+ m_input_recorder(nullptr),
+ m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
+ m_terminal_state(), m_target_list(*this), m_platform_list(),
+ m_listener_sp(Listener::MakeListener("lldb.Debugger")),
+ m_source_manager_up(), m_source_file_cache(),
+ m_command_interpreter_up(
+ std::make_unique<CommandInterpreter>(*this, false)),
+ m_script_interpreter_sp(), m_input_reader_stack(), m_instance_name(),
+ m_loaded_plugins(), m_event_handler_thread(), m_io_handler_thread(),
+ m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
+ m_forward_listener_sp(), m_clear_once() {
+ char instance_cstr[256];
+ snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID());
+ m_instance_name.SetCString(instance_cstr);
+ if (log_callback)
+ m_log_callback_stream_sp =
+ std::make_shared<StreamCallback>(log_callback, baton);
+ m_command_interpreter_up->Initialize();
+ // Always add our default platform to the platform list
+ PlatformSP default_platform_sp(Platform::GetHostPlatform());
+ assert(default_platform_sp);
+ m_platform_list.Append(default_platform_sp, true);
+
+ m_dummy_target_sp = m_target_list.GetDummyTarget(*this);
+ assert(m_dummy_target_sp.get() && "Couldn't construct dummy target?");
+
+ m_collection_sp->Initialize(g_debugger_properties);
+ m_collection_sp->AppendProperty(
+ ConstString("target"),
+ ConstString("Settings specify to debugging targets."), true,
+ Target::GetGlobalProperties()->GetValueProperties());
+ m_collection_sp->AppendProperty(
+ ConstString("platform"), ConstString("Platform settings."), true,
+ Platform::GetGlobalPlatformProperties()->GetValueProperties());
+ m_collection_sp->AppendProperty(
+ ConstString("symbols"), ConstString("Symbol lookup and cache settings."),
+ true, ModuleList::GetGlobalModuleListProperties().GetValueProperties());
+ if (m_command_interpreter_up) {
+ m_collection_sp->AppendProperty(
+ ConstString("interpreter"),
+ ConstString("Settings specify to the debugger's command interpreter."),
+ true, m_command_interpreter_up->GetValueProperties());
+ }
+ OptionValueSInt64 *term_width =
+ m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64(
+ nullptr, ePropertyTerminalWidth);
+ term_width->SetMinimumValue(10);
+ term_width->SetMaximumValue(1024);
+
+ // Turn off use-color if this is a dumb terminal.
+ const char *term = getenv("TERM");
+ if (term && !strcmp(term, "dumb"))
+ SetUseColor(false);
+ // Turn off use-color if we don't write to a terminal with color support.
+ if (!GetOutputFile().GetIsTerminalWithColors())
+ SetUseColor(false);
+
+#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ // Enabling use of ANSI color codes because LLDB is using them to highlight
+ // text.
+ llvm::sys::Process::UseANSIEscapeCodes(true);
+#endif
+}
+
+Debugger::~Debugger() { Clear(); }
+
+void Debugger::Clear() {
+ // Make sure we call this function only once. With the C++ global destructor
+ // chain having a list of debuggers and with code that can be running on
+ // other threads, we need to ensure this doesn't happen multiple times.
+ //
+ // The following functions call Debugger::Clear():
+ // Debugger::~Debugger();
+ // static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
+ // static void Debugger::Terminate();
+ llvm::call_once(m_clear_once, [this]() {
+ ClearIOHandlers();
+ StopIOHandlerThread();
+ StopEventHandlerThread();
+ m_listener_sp->Clear();
+ int num_targets = m_target_list.GetNumTargets();
+ for (int i = 0; i < num_targets; i++) {
+ TargetSP target_sp(m_target_list.GetTargetAtIndex(i));
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ if (process_sp)
+ process_sp->Finalize();
+ target_sp->Destroy();
+ }
+ }
+ m_broadcaster_manager_sp->Clear();
+
+ // Close the input file _before_ we close the input read communications
+ // class as it does NOT own the input file, our m_input_file does.
+ m_terminal_state.Clear();
+ GetInputFile().Close();
+
+ m_command_interpreter_up->Clear();
+ });
+}
+
+bool Debugger::GetCloseInputOnEOF() const {
+ // return m_input_comm.GetCloseOnEOF();
+ return false;
+}
+
+void Debugger::SetCloseInputOnEOF(bool b) {
+ // m_input_comm.SetCloseOnEOF(b);
+}
+
+bool Debugger::GetAsyncExecution() {
+ return !m_command_interpreter_up->GetSynchronous();
+}
+
+void Debugger::SetAsyncExecution(bool async_execution) {
+ m_command_interpreter_up->SetSynchronous(!async_execution);
+}
+
+repro::DataRecorder *Debugger::GetInputRecorder() { return m_input_recorder; }
+
+void Debugger::SetInputFile(FileSP file_sp, repro::DataRecorder *recorder) {
+ assert(file_sp && file_sp->IsValid());
+ m_input_recorder = recorder;
+ m_input_file_sp = file_sp;
+ // Save away the terminal state if that is relevant, so that we can restore
+ // it in RestoreInputState.
+ SaveInputTerminalState();
+}
+
+void Debugger::SetOutputFile(FileSP file_sp) {
+ assert(file_sp && file_sp->IsValid());
+ m_output_stream_sp = std::make_shared<StreamFile>(file_sp);
+}
+
+void Debugger::SetErrorFile(FileSP file_sp) {
+ assert(file_sp && file_sp->IsValid());
+ m_error_stream_sp = std::make_shared<StreamFile>(file_sp);
+}
+
+void Debugger::SaveInputTerminalState() {
+ int fd = GetInputFile().GetDescriptor();
+ if (fd != File::kInvalidDescriptor)
+ m_terminal_state.Save(fd, true);
+}
+
+void Debugger::RestoreInputTerminalState() { m_terminal_state.Restore(); }
+
+ExecutionContext Debugger::GetSelectedExecutionContext() {
+ ExecutionContext exe_ctx;
+ TargetSP target_sp(GetSelectedTarget());
+ exe_ctx.SetTargetSP(target_sp);
+
+ if (target_sp) {
+ ProcessSP process_sp(target_sp->GetProcessSP());
+ exe_ctx.SetProcessSP(process_sp);
+ if (process_sp && !process_sp->IsRunning()) {
+ ThreadSP thread_sp(process_sp->GetThreadList().GetSelectedThread());
+ if (thread_sp) {
+ exe_ctx.SetThreadSP(thread_sp);
+ exe_ctx.SetFrameSP(thread_sp->GetSelectedFrame());
+ if (exe_ctx.GetFramePtr() == nullptr)
+ exe_ctx.SetFrameSP(thread_sp->GetStackFrameAtIndex(0));
+ }
+ }
+ }
+ return exe_ctx;
+}
+
+void Debugger::DispatchInputInterrupt() {
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (reader_sp)
+ reader_sp->Interrupt();
+}
+
+void Debugger::DispatchInputEndOfFile() {
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (reader_sp)
+ reader_sp->GotEOF();
+}
+
+void Debugger::ClearIOHandlers() {
+ // The bottom input reader should be the main debugger input reader. We do
+ // not want to close that one here.
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ while (m_input_reader_stack.GetSize() > 1) {
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (reader_sp)
+ PopIOHandler(reader_sp);
+ }
+}
+
+void Debugger::ExecuteIOHandlers() {
+ while (true) {
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+ if (!reader_sp)
+ break;
+
+ reader_sp->Run();
+
+ // Remove all input readers that are done from the top of the stack
+ while (true) {
+ IOHandlerSP top_reader_sp = m_input_reader_stack.Top();
+ if (top_reader_sp && top_reader_sp->GetIsDone())
+ PopIOHandler(top_reader_sp);
+ else
+ break;
+ }
+ }
+ ClearIOHandlers();
+}
+
+bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) {
+ return m_input_reader_stack.IsTop(reader_sp);
+}
+
+bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type,
+ IOHandler::Type second_top_type) {
+ return m_input_reader_stack.CheckTopIOHandlerTypes(top_type, second_top_type);
+}
+
+void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) {
+ lldb_private::StreamFile &stream =
+ is_stdout ? GetOutputStream() : GetErrorStream();
+ m_input_reader_stack.PrintAsync(&stream, s, len);
+}
+
+ConstString Debugger::GetTopIOHandlerControlSequence(char ch) {
+ return m_input_reader_stack.GetTopIOHandlerControlSequence(ch);
+}
+
+const char *Debugger::GetIOHandlerCommandPrefix() {
+ return m_input_reader_stack.GetTopIOHandlerCommandPrefix();
+}
+
+const char *Debugger::GetIOHandlerHelpPrologue() {
+ return m_input_reader_stack.GetTopIOHandlerHelpPrologue();
+}
+
+void Debugger::RunIOHandler(const IOHandlerSP &reader_sp) {
+ PushIOHandler(reader_sp);
+
+ IOHandlerSP top_reader_sp = reader_sp;
+ while (top_reader_sp) {
+ top_reader_sp->Run();
+
+ if (top_reader_sp.get() == reader_sp.get()) {
+ if (PopIOHandler(reader_sp))
+ break;
+ }
+
+ while (true) {
+ top_reader_sp = m_input_reader_stack.Top();
+ if (top_reader_sp && top_reader_sp->GetIsDone())
+ PopIOHandler(top_reader_sp);
+ else
+ break;
+ }
+ }
+}
+
+void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out,
+ StreamFileSP &err) {
+ // Before an IOHandler runs, it must have in/out/err streams. This function
+ // is called when one ore more of the streams are nullptr. We use the top
+ // input reader's in/out/err streams, or fall back to the debugger file
+ // handles, or we fall back onto stdin/stdout/stderr as a last resort.
+
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+ IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
+ // If no STDIN has been set, then set it appropriately
+ if (!in || !in->IsValid()) {
+ if (top_reader_sp)
+ in = top_reader_sp->GetInputFileSP();
+ else
+ in = GetInputFileSP();
+ // If there is nothing, use stdin
+ if (!in)
+ in = std::make_shared<NativeFile>(stdin, false);
+ }
+ // If no STDOUT has been set, then set it appropriately
+ if (!out || !out->GetFile().IsValid()) {
+ if (top_reader_sp)
+ out = top_reader_sp->GetOutputStreamFileSP();
+ else
+ out = GetOutputStreamSP();
+ // If there is nothing, use stdout
+ if (!out)
+ out = std::make_shared<StreamFile>(stdout, false);
+ }
+ // If no STDERR has been set, then set it appropriately
+ if (!err || !err->GetFile().IsValid()) {
+ if (top_reader_sp)
+ err = top_reader_sp->GetErrorStreamFileSP();
+ else
+ err = GetErrorStreamSP();
+ // If there is nothing, use stderr
+ if (!err)
+ err = std::make_shared<StreamFile>(stderr, false);
+ }
+}
+
+void Debugger::PushIOHandler(const IOHandlerSP &reader_sp,
+ bool cancel_top_handler) {
+ if (!reader_sp)
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+
+ // Get the current top input reader...
+ IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
+
+ // Don't push the same IO handler twice...
+ if (reader_sp == top_reader_sp)
+ return;
+
+ // Push our new input reader
+ m_input_reader_stack.Push(reader_sp);
+ reader_sp->Activate();
+
+ // Interrupt the top input reader to it will exit its Run() function and let
+ // this new input reader take over
+ if (top_reader_sp) {
+ top_reader_sp->Deactivate();
+ if (cancel_top_handler)
+ top_reader_sp->Cancel();
+ }
+}
+
+bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) {
+ if (!pop_reader_sp)
+ return false;
+
+ std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
+
+ // The reader on the stop of the stack is done, so let the next read on the
+ // stack refresh its prompt and if there is one...
+ if (m_input_reader_stack.IsEmpty())
+ return false;
+
+ IOHandlerSP reader_sp(m_input_reader_stack.Top());
+
+ if (pop_reader_sp != reader_sp)
+ return false;
+
+ reader_sp->Deactivate();
+ reader_sp->Cancel();
+ m_input_reader_stack.Pop();
+
+ reader_sp = m_input_reader_stack.Top();
+ if (reader_sp)
+ reader_sp->Activate();
+
+ return true;
+}
+
+StreamSP Debugger::GetAsyncOutputStream() {
+ return std::make_shared<StreamAsynchronousIO>(*this, true);
+}
+
+StreamSP Debugger::GetAsyncErrorStream() {
+ return std::make_shared<StreamAsynchronousIO>(*this, false);
+}
+
+size_t Debugger::GetNumDebuggers() {
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ return g_debugger_list_ptr->size();
+ }
+ return 0;
+}
+
+lldb::DebuggerSP Debugger::GetDebuggerAtIndex(size_t index) {
+ DebuggerSP debugger_sp;
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ if (index < g_debugger_list_ptr->size())
+ debugger_sp = g_debugger_list_ptr->at(index);
+ }
+
+ return debugger_sp;
+}
+
+DebuggerSP Debugger::FindDebuggerWithID(lldb::user_id_t id) {
+ DebuggerSP debugger_sp;
+
+ if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+ DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
+ for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
+ if ((*pos)->GetID() == id) {
+ debugger_sp = *pos;
+ break;
+ }
+ }
+ }
+ return debugger_sp;
+}
+
+bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format,
+ const SymbolContext *sc,
+ const SymbolContext *prev_sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr, Stream &s) {
+ FormatEntity::Entry format_entry;
+
+ if (format == nullptr) {
+ if (exe_ctx != nullptr && exe_ctx->HasTargetScope())
+ format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ if (format == nullptr) {
+ FormatEntity::Parse("${addr}: ", format_entry);
+ format = &format_entry;
+ }
+ }
+ bool function_changed = false;
+ bool initial_function = false;
+ if (prev_sc && (prev_sc->function || prev_sc->symbol)) {
+ if (sc && (sc->function || sc->symbol)) {
+ if (prev_sc->symbol && sc->symbol) {
+ if (!sc->symbol->Compare(prev_sc->symbol->GetName(),
+ prev_sc->symbol->GetType())) {
+ function_changed = true;
+ }
+ } else if (prev_sc->function && sc->function) {
+ if (prev_sc->function->GetMangled() != sc->function->GetMangled()) {
+ function_changed = true;
+ }
+ }
+ }
+ }
+ // The first context on a list of instructions will have a prev_sc that has
+ // no Function or Symbol -- if SymbolContext had an IsValid() method, it
+ // would return false. But we do get a prev_sc pointer.
+ if ((sc && (sc->function || sc->symbol)) && prev_sc &&
+ (prev_sc->function == nullptr && prev_sc->symbol == nullptr)) {
+ initial_function = true;
+ }
+ return FormatEntity::Format(*format, s, sc, exe_ctx, addr, nullptr,
+ function_changed, initial_function);
+}
+
+void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
+ void *baton) {
+ // For simplicity's sake, I am not going to deal with how to close down any
+ // open logging streams, I just redirect everything from here on out to the
+ // callback.
+ m_log_callback_stream_sp =
+ std::make_shared<StreamCallback>(log_callback, baton);
+}
+
+bool Debugger::EnableLog(llvm::StringRef channel,
+ llvm::ArrayRef<const char *> categories,
+ llvm::StringRef log_file, uint32_t log_options,
+ llvm::raw_ostream &error_stream) {
+ const bool should_close = true;
+ const bool unbuffered = true;
+
+ std::shared_ptr<llvm::raw_ostream> log_stream_sp;
+ if (m_log_callback_stream_sp) {
+ log_stream_sp = m_log_callback_stream_sp;
+ // For now when using the callback mode you always get thread & timestamp.
+ log_options |=
+ LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
+ } else if (log_file.empty()) {
+ log_stream_sp = std::make_shared<llvm::raw_fd_ostream>(
+ GetOutputFile().GetDescriptor(), !should_close, unbuffered);
+ } else {
+ auto pos = m_log_streams.find(log_file);
+ if (pos != m_log_streams.end())
+ log_stream_sp = pos->second.lock();
+ if (!log_stream_sp) {
+ llvm::sys::fs::OpenFlags flags = llvm::sys::fs::OF_Text;
+ if (log_options & LLDB_LOG_OPTION_APPEND)
+ flags |= llvm::sys::fs::OF_Append;
+ int FD;
+ if (std::error_code ec = llvm::sys::fs::openFileForWrite(
+ log_file, FD, llvm::sys::fs::CD_CreateAlways, flags)) {
+ error_stream << "Unable to open log file: " << ec.message();
+ return false;
+ }
+ log_stream_sp =
+ std::make_shared<llvm::raw_fd_ostream>(FD, should_close, unbuffered);
+ m_log_streams[log_file] = log_stream_sp;
+ }
+ }
+ assert(log_stream_sp);
+
+ if (log_options == 0)
+ log_options =
+ LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE;
+
+ return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories,
+ error_stream);
+}
+
+ScriptInterpreter *Debugger::GetScriptInterpreter(bool can_create) {
+ std::lock_guard<std::recursive_mutex> locker(m_script_interpreter_mutex);
+
+ if (!m_script_interpreter_sp) {
+ if (!can_create)
+ return nullptr;
+ m_script_interpreter_sp = PluginManager::GetScriptInterpreterForLanguage(
+ GetScriptLanguage(), *this);
+ }
+
+ return m_script_interpreter_sp.get();
+}
+
+SourceManager &Debugger::GetSourceManager() {
+ if (!m_source_manager_up)
+ m_source_manager_up = std::make_unique<SourceManager>(shared_from_this());
+ return *m_source_manager_up;
+}
+
+// This function handles events that were broadcast by the process.
+void Debugger::HandleBreakpointEvent(const EventSP &event_sp) {
+ using namespace lldb;
+ const uint32_t event_type =
+ Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent(
+ event_sp);
+
+ // if (event_type & eBreakpointEventTypeAdded
+ // || event_type & eBreakpointEventTypeRemoved
+ // || event_type & eBreakpointEventTypeEnabled
+ // || event_type & eBreakpointEventTypeDisabled
+ // || event_type & eBreakpointEventTypeCommandChanged
+ // || event_type & eBreakpointEventTypeConditionChanged
+ // || event_type & eBreakpointEventTypeIgnoreChanged
+ // || event_type & eBreakpointEventTypeLocationsResolved)
+ // {
+ // // Don't do anything about these events, since the breakpoint
+ // commands already echo these actions.
+ // }
+ //
+ if (event_type & eBreakpointEventTypeLocationsAdded) {
+ uint32_t num_new_locations =
+ Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(
+ event_sp);
+ if (num_new_locations > 0) {
+ BreakpointSP breakpoint =
+ Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp);
+ StreamSP output_sp(GetAsyncOutputStream());
+ if (output_sp) {
+ output_sp->Printf("%d location%s added to breakpoint %d\n",
+ num_new_locations, num_new_locations == 1 ? "" : "s",
+ breakpoint->GetID());
+ output_sp->Flush();
+ }
+ }
+ }
+ // else if (event_type & eBreakpointEventTypeLocationsRemoved)
+ // {
+ // // These locations just get disabled, not sure it is worth spamming
+ // folks about this on the command line.
+ // }
+ // else if (event_type & eBreakpointEventTypeLocationsResolved)
+ // {
+ // // This might be an interesting thing to note, but I'm going to
+ // leave it quiet for now, it just looked noisy.
+ // }
+}
+
+void Debugger::FlushProcessOutput(Process &process, bool flush_stdout,
+ bool flush_stderr) {
+ const auto &flush = [&](Stream &stream,
+ size_t (Process::*get)(char *, size_t, Status &)) {
+ Status error;
+ size_t len;
+ char buffer[1024];
+ while ((len = (process.*get)(buffer, sizeof(buffer), error)) > 0)
+ stream.Write(buffer, len);
+ stream.Flush();
+ };
+
+ std::lock_guard<std::mutex> guard(m_output_flush_mutex);
+ if (flush_stdout)
+ flush(*GetAsyncOutputStream(), &Process::GetSTDOUT);
+ if (flush_stderr)
+ flush(*GetAsyncErrorStream(), &Process::GetSTDERR);
+}
+
+// This function handles events that were broadcast by the process.
+void Debugger::HandleProcessEvent(const EventSP &event_sp) {
+ using namespace lldb;
+ const uint32_t event_type = event_sp->GetType();
+ ProcessSP process_sp =
+ (event_type == Process::eBroadcastBitStructuredData)
+ ? EventDataStructuredData::GetProcessFromEvent(event_sp.get())
+ : Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
+
+ StreamSP output_stream_sp = GetAsyncOutputStream();
+ StreamSP error_stream_sp = GetAsyncErrorStream();
+ const bool gui_enabled = IsForwardingEvents();
+
+ if (!gui_enabled) {
+ bool pop_process_io_handler = false;
+ assert(process_sp);
+
+ bool state_is_stopped = false;
+ const bool got_state_changed =
+ (event_type & Process::eBroadcastBitStateChanged) != 0;
+ const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0;
+ const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0;
+ const bool got_structured_data =
+ (event_type & Process::eBroadcastBitStructuredData) != 0;
+
+ if (got_state_changed) {
+ StateType event_state =
+ Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ state_is_stopped = StateIsStoppedState(event_state, false);
+ }
+
+ // Display running state changes first before any STDIO
+ if (got_state_changed && !state_is_stopped) {
+ Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(),
+ pop_process_io_handler);
+ }
+
+ // Now display STDOUT and STDERR
+ FlushProcessOutput(*process_sp, got_stdout || got_state_changed,
+ got_stderr || got_state_changed);
+
+ // Give structured data events an opportunity to display.
+ if (got_structured_data) {
+ StructuredDataPluginSP plugin_sp =
+ EventDataStructuredData::GetPluginFromEvent(event_sp.get());
+ if (plugin_sp) {
+ auto structured_data_sp =
+ EventDataStructuredData::GetObjectFromEvent(event_sp.get());
+ if (output_stream_sp) {
+ StreamString content_stream;
+ Status error =
+ plugin_sp->GetDescription(structured_data_sp, content_stream);
+ if (error.Success()) {
+ if (!content_stream.GetString().empty()) {
+ // Add newline.
+ content_stream.PutChar('\n');
+ content_stream.Flush();
+
+ // Print it.
+ output_stream_sp->PutCString(content_stream.GetString());
+ }
+ } else {
+ error_stream_sp->Printf("Failed to print structured "
+ "data with plugin %s: %s",
+ plugin_sp->GetPluginName().AsCString(),
+ error.AsCString());
+ }
+ }
+ }
+ }
+
+ // Now display any stopped state changes after any STDIO
+ if (got_state_changed && state_is_stopped) {
+ Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(),
+ pop_process_io_handler);
+ }
+
+ output_stream_sp->Flush();
+ error_stream_sp->Flush();
+
+ if (pop_process_io_handler)
+ process_sp->PopProcessIOHandler();
+ }
+}
+
+void Debugger::HandleThreadEvent(const EventSP &event_sp) {
+ // At present the only thread event we handle is the Frame Changed event, and
+ // all we do for that is just reprint the thread status for that thread.
+ using namespace lldb;
+ const uint32_t event_type = event_sp->GetType();
+ const bool stop_format = true;
+ if (event_type == Thread::eBroadcastBitStackChanged ||
+ event_type == Thread::eBroadcastBitThreadSelected) {
+ ThreadSP thread_sp(
+ Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()));
+ if (thread_sp) {
+ thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format);
+ }
+ }
+}
+
+bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; }
+
+void Debugger::EnableForwardEvents(const ListenerSP &listener_sp) {
+ m_forward_listener_sp = listener_sp;
+}
+
+void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) {
+ m_forward_listener_sp.reset();
+}
+
+void Debugger::DefaultEventHandler() {
+ ListenerSP listener_sp(GetListener());
+ ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
+ BroadcastEventSpec target_event_spec(broadcaster_class_target,
+ Target::eBroadcastBitBreakpointChanged);
+
+ BroadcastEventSpec process_event_spec(
+ broadcaster_class_process,
+ Process::eBroadcastBitStateChanged | Process::eBroadcastBitSTDOUT |
+ Process::eBroadcastBitSTDERR | Process::eBroadcastBitStructuredData);
+
+ BroadcastEventSpec thread_event_spec(broadcaster_class_thread,
+ Thread::eBroadcastBitStackChanged |
+ Thread::eBroadcastBitThreadSelected);
+
+ listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
+ target_event_spec);
+ listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
+ process_event_spec);
+ listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
+ thread_event_spec);
+ listener_sp->StartListeningForEvents(
+ m_command_interpreter_up.get(),
+ CommandInterpreter::eBroadcastBitQuitCommandReceived |
+ CommandInterpreter::eBroadcastBitAsynchronousOutputData |
+ CommandInterpreter::eBroadcastBitAsynchronousErrorData);
+
+ // Let the thread that spawned us know that we have started up and that we
+ // are now listening to all required events so no events get missed
+ m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening);
+
+ bool done = false;
+ while (!done) {
+ EventSP event_sp;
+ if (listener_sp->GetEvent(event_sp, llvm::None)) {
+ if (event_sp) {
+ Broadcaster *broadcaster = event_sp->GetBroadcaster();
+ if (broadcaster) {
+ uint32_t event_type = event_sp->GetType();
+ ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
+ if (broadcaster_class == broadcaster_class_process) {
+ HandleProcessEvent(event_sp);
+ } else if (broadcaster_class == broadcaster_class_target) {
+ if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
+ event_sp.get())) {
+ HandleBreakpointEvent(event_sp);
+ }
+ } else if (broadcaster_class == broadcaster_class_thread) {
+ HandleThreadEvent(event_sp);
+ } else if (broadcaster == m_command_interpreter_up.get()) {
+ if (event_type &
+ CommandInterpreter::eBroadcastBitQuitCommandReceived) {
+ done = true;
+ } else if (event_type &
+ CommandInterpreter::eBroadcastBitAsynchronousErrorData) {
+ const char *data = reinterpret_cast<const char *>(
+ EventDataBytes::GetBytesFromEvent(event_sp.get()));
+ if (data && data[0]) {
+ StreamSP error_sp(GetAsyncErrorStream());
+ if (error_sp) {
+ error_sp->PutCString(data);
+ error_sp->Flush();
+ }
+ }
+ } else if (event_type & CommandInterpreter::
+ eBroadcastBitAsynchronousOutputData) {
+ const char *data = reinterpret_cast<const char *>(
+ EventDataBytes::GetBytesFromEvent(event_sp.get()));
+ if (data && data[0]) {
+ StreamSP output_sp(GetAsyncOutputStream());
+ if (output_sp) {
+ output_sp->PutCString(data);
+ output_sp->Flush();
+ }
+ }
+ }
+ }
+ }
+
+ if (m_forward_listener_sp)
+ m_forward_listener_sp->AddEvent(event_sp);
+ }
+ }
+ }
+}
+
+lldb::thread_result_t Debugger::EventHandlerThread(lldb::thread_arg_t arg) {
+ ((Debugger *)arg)->DefaultEventHandler();
+ return {};
+}
+
+bool Debugger::StartEventHandlerThread() {
+ if (!m_event_handler_thread.IsJoinable()) {
+ // We must synchronize with the DefaultEventHandler() thread to ensure it
+ // is up and running and listening to events before we return from this
+ // function. We do this by listening to events for the
+ // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster
+ ConstString full_name("lldb.debugger.event-handler");
+ ListenerSP listener_sp(Listener::MakeListener(full_name.AsCString()));
+ listener_sp->StartListeningForEvents(&m_sync_broadcaster,
+ eBroadcastBitEventThreadIsListening);
+
+ auto thread_name =
+ full_name.GetLength() < llvm::get_max_thread_name_length()
+ ? full_name.AsCString()
+ : "dbg.evt-handler";
+
+ // Use larger 8MB stack for this thread
+ llvm::Expected<HostThread> event_handler_thread =
+ ThreadLauncher::LaunchThread(thread_name, EventHandlerThread, this,
+ g_debugger_event_thread_stack_bytes);
+
+ if (event_handler_thread) {
+ m_event_handler_thread = *event_handler_thread;
+ } else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(event_handler_thread.takeError()));
+ }
+
+ // Make sure DefaultEventHandler() is running and listening to events
+ // before we return from this function. We are only listening for events of
+ // type eBroadcastBitEventThreadIsListening so we don't need to check the
+ // event, we just need to wait an infinite amount of time for it (nullptr
+ // timeout as the first parameter)
+ lldb::EventSP event_sp;
+ listener_sp->GetEvent(event_sp, llvm::None);
+ }
+ return m_event_handler_thread.IsJoinable();
+}
+
+void Debugger::StopEventHandlerThread() {
+ if (m_event_handler_thread.IsJoinable()) {
+ GetCommandInterpreter().BroadcastEvent(
+ CommandInterpreter::eBroadcastBitQuitCommandReceived);
+ m_event_handler_thread.Join(nullptr);
+ }
+}
+
+lldb::thread_result_t Debugger::IOHandlerThread(lldb::thread_arg_t arg) {
+ Debugger *debugger = (Debugger *)arg;
+ debugger->ExecuteIOHandlers();
+ debugger->StopEventHandlerThread();
+ return {};
+}
+
+bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
+
+bool Debugger::StartIOHandlerThread() {
+ if (!m_io_handler_thread.IsJoinable()) {
+ llvm::Expected<HostThread> io_handler_thread = ThreadLauncher::LaunchThread(
+ "lldb.debugger.io-handler", IOHandlerThread, this,
+ 8 * 1024 * 1024); // Use larger 8MB stack for this thread
+ if (io_handler_thread) {
+ m_io_handler_thread = *io_handler_thread;
+ } else {
+ LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
+ "failed to launch host thread: {}",
+ llvm::toString(io_handler_thread.takeError()));
+ }
+ }
+ return m_io_handler_thread.IsJoinable();
+}
+
+void Debugger::StopIOHandlerThread() {
+ if (m_io_handler_thread.IsJoinable()) {
+ GetInputFile().Close();
+ m_io_handler_thread.Join(nullptr);
+ }
+}
+
+void Debugger::JoinIOHandlerThread() {
+ if (HasIOHandlerThread()) {
+ thread_result_t result;
+ m_io_handler_thread.Join(&result);
+ m_io_handler_thread = LLDB_INVALID_HOST_THREAD;
+ }
+}
+
+Target *Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) {
+ Target *target = nullptr;
+ if (!prefer_dummy) {
+ target = m_target_list.GetSelectedTarget().get();
+ if (target)
+ return target;
+ }
+
+ return GetDummyTarget();
+}
+
+Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
+ Status err;
+ FileSpec repl_executable;
+
+ if (language == eLanguageTypeUnknown) {
+ LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs();
+
+ if (auto single_lang = repl_languages.GetSingularLanguage()) {
+ language = *single_lang;
+ } else if (repl_languages.Empty()) {
+ err.SetErrorStringWithFormat(
+ "LLDB isn't configured with REPL support for any languages.");
+ return err;
+ } else {
+ err.SetErrorStringWithFormat(
+ "Multiple possible REPL languages. Please specify a language.");
+ return err;
+ }
+ }
+
+ Target *const target =
+ nullptr; // passing in an empty target means the REPL must create one
+
+ REPLSP repl_sp(REPL::Create(err, language, this, target, repl_options));
+
+ if (!err.Success()) {
+ return err;
+ }
+
+ if (!repl_sp) {
+ err.SetErrorStringWithFormat("couldn't find a REPL for %s",
+ Language::GetNameForLanguageType(language));
+ return err;
+ }
+
+ repl_sp->SetCompilerOptions(repl_options);
+ repl_sp->RunLoop();
+
+ return err;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Disassembler.cpp b/contrib/llvm-project/lldb/source/Core/Disassembler.cpp
new file mode 100644
index 000000000000..89ae25cbad64
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Disassembler.cpp
@@ -0,0 +1,1466 @@
+//===-- Disassembler.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Disassembler.h"
+
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Interpreter/OptionValueRegex.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "lldb/lldb-private-interfaces.h"
+#include "lldb/lldb-private-types.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+
+#include <cstdint>
+#include <cstring>
+#include <utility>
+
+#include <assert.h>
+
+#define DEFAULT_DISASM_BYTE_SIZE 32
+
+using namespace lldb;
+using namespace lldb_private;
+
+DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch,
+ const char *flavor,
+ const char *plugin_name) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Disassembler::FindPlugin (arch = %s, plugin_name = %s)",
+ arch.GetArchitectureName(), plugin_name);
+
+ DisassemblerCreateInstance create_callback = nullptr;
+
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback = PluginManager::GetDisassemblerCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ DisassemblerSP disassembler_sp(create_callback(arch, flavor));
+
+ if (disassembler_sp)
+ return disassembler_sp;
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(
+ idx)) != nullptr;
+ ++idx) {
+ DisassemblerSP disassembler_sp(create_callback(arch, flavor));
+
+ if (disassembler_sp)
+ return disassembler_sp;
+ }
+ }
+ return DisassemblerSP();
+}
+
+DisassemblerSP Disassembler::FindPluginForTarget(const TargetSP target_sp,
+ const ArchSpec &arch,
+ const char *flavor,
+ const char *plugin_name) {
+ if (target_sp && flavor == nullptr) {
+ // FIXME - we don't have the mechanism in place to do per-architecture
+ // settings. But since we know that for now we only support flavors on x86
+ // & x86_64,
+ if (arch.GetTriple().getArch() == llvm::Triple::x86 ||
+ arch.GetTriple().getArch() == llvm::Triple::x86_64)
+ flavor = target_sp->GetDisassemblyFlavor();
+ }
+ return FindPlugin(arch, flavor, plugin_name);
+}
+
+static void ResolveAddress(const ExecutionContext &exe_ctx, const Address &addr,
+ Address &resolved_addr) {
+ if (!addr.IsSectionOffset()) {
+ // If we weren't passed in a section offset address range, try and resolve
+ // it to something
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ bool is_resolved =
+ target->GetSectionLoadList().IsEmpty() ?
+ target->GetImages().ResolveFileAddress(addr.GetOffset(),
+ resolved_addr) :
+ target->GetSectionLoadList().ResolveLoadAddress(addr.GetOffset(),
+ resolved_addr);
+
+ // We weren't able to resolve the address, just treat it as a raw address
+ if (is_resolved && resolved_addr.IsValid())
+ return;
+ }
+ }
+ resolved_addr = addr;
+}
+
+size_t Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ SymbolContextList &sc_list,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ size_t success_count = 0;
+ const size_t count = sc_list.GetSize();
+ SymbolContext sc;
+ AddressRange range;
+ const uint32_t scope =
+ eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = true;
+ for (size_t i = 0; i < count; ++i) {
+ if (!sc_list.GetContextAtIndex(i, sc))
+ break;
+ for (uint32_t range_idx = 0;
+ sc.GetAddressRange(scope, range_idx, use_inline_block_range, range);
+ ++range_idx) {
+ if (Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm)) {
+ ++success_count;
+ strm.EOL();
+ }
+ }
+ }
+ return success_count;
+}
+
+bool Disassembler::Disassemble(
+ Debugger &debugger, const ArchSpec &arch, const char *plugin_name,
+ const char *flavor, const ExecutionContext &exe_ctx, ConstString name,
+ Module *module, uint32_t num_instructions, bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines, uint32_t options, Stream &strm) {
+ // If no name is given there's nothing to disassemble.
+ if (!name)
+ return false;
+
+ const bool include_symbols = true;
+ const bool include_inlines = true;
+
+ // Find functions matching the given name.
+ SymbolContextList sc_list;
+ if (module) {
+ module->FindFunctions(name, nullptr, eFunctionNameTypeAuto, include_symbols,
+ include_inlines, sc_list);
+ } else if (exe_ctx.GetTargetPtr()) {
+ exe_ctx.GetTargetPtr()->GetImages().FindFunctions(
+ name, eFunctionNameTypeAuto, include_symbols, include_inlines, sc_list);
+ }
+
+ // If no functions were found there's nothing to disassemble.
+ if (sc_list.IsEmpty())
+ return false;
+
+ return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, sc_list,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+}
+
+lldb::DisassemblerSP Disassembler::DisassembleRange(
+ const ArchSpec &arch, const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx, const AddressRange &range,
+ bool prefer_file_cache) {
+ if (range.GetByteSize() <= 0)
+ return {};
+
+ if (!range.GetBaseAddress().IsValid())
+ return {};
+
+ lldb::DisassemblerSP disasm_sp = Disassembler::FindPluginForTarget(
+ exe_ctx.GetTargetSP(), arch, flavor, plugin_name);
+
+ if (!disasm_sp)
+ return {};
+
+ const size_t bytes_disassembled =
+ disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache);
+ if (bytes_disassembled == 0)
+ return {};
+
+ return disasm_sp;
+}
+
+lldb::DisassemblerSP
+Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,
+ const char *flavor, const Address &start,
+ const void *src, size_t src_len,
+ uint32_t num_instructions, bool data_from_file) {
+ if (!src)
+ return {};
+
+ lldb::DisassemblerSP disasm_sp =
+ Disassembler::FindPlugin(arch, flavor, plugin_name);
+
+ if (!disasm_sp)
+ return {};
+
+ DataExtractor data(src, src_len, arch.GetByteOrder(),
+ arch.GetAddressByteSize());
+
+ (void)disasm_sp->DecodeInstructions(start, data, 0, num_instructions, false,
+ data_from_file);
+ return disasm_sp;
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ const AddressRange &disasm_range,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ if (!disasm_range.GetByteSize())
+ return false;
+
+ lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget(
+ exe_ctx.GetTargetSP(), arch, flavor, plugin_name));
+
+ if (!disasm_sp)
+ return false;
+
+ AddressRange range;
+ ResolveAddress(exe_ctx, disasm_range.GetBaseAddress(),
+ range.GetBaseAddress());
+ range.SetByteSize(disasm_range.GetByteSize());
+ const bool prefer_file_cache = false;
+ size_t bytes_disassembled =
+ disasm_sp->ParseInstructions(&exe_ctx, range, &strm, prefer_file_cache);
+ if (bytes_disassembled == 0)
+ return false;
+
+ return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ const Address &start_address,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ if (num_instructions == 0)
+ return false;
+
+ lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget(
+ exe_ctx.GetTargetSP(), arch, flavor, plugin_name));
+ if (!disasm_sp)
+ return false;
+
+ Address addr;
+ ResolveAddress(exe_ctx, start_address, addr);
+
+ const bool prefer_file_cache = false;
+ size_t bytes_disassembled = disasm_sp->ParseInstructions(
+ &exe_ctx, addr, num_instructions, prefer_file_cache);
+ if (bytes_disassembled == 0)
+ return false;
+
+ return PrintInstructions(disasm_sp.get(), debugger, arch, exe_ctx,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+}
+
+Disassembler::SourceLine
+Disassembler::GetFunctionDeclLineEntry(const SymbolContext &sc) {
+ if (!sc.function)
+ return {};
+
+ if (!sc.line_entry.IsValid())
+ return {};
+
+ LineEntry prologue_end_line = sc.line_entry;
+ FileSpec func_decl_file;
+ uint32_t func_decl_line;
+ sc.function->GetStartLineSourceInfo(func_decl_file, func_decl_line);
+
+ if (func_decl_file != prologue_end_line.file &&
+ func_decl_file != prologue_end_line.original_file)
+ return {};
+
+ SourceLine decl_line;
+ decl_line.file = func_decl_file;
+ decl_line.line = func_decl_line;
+ // TODO: Do we care about column on these entries? If so, we need to plumb
+ // that through GetStartLineSourceInfo.
+ decl_line.column = 0;
+ return decl_line;
+}
+
+void Disassembler::AddLineToSourceLineTables(
+ SourceLine &line,
+ std::map<FileSpec, std::set<uint32_t>> &source_lines_seen) {
+ if (line.IsValid()) {
+ auto source_lines_seen_pos = source_lines_seen.find(line.file);
+ if (source_lines_seen_pos == source_lines_seen.end()) {
+ std::set<uint32_t> lines;
+ lines.insert(line.line);
+ source_lines_seen.emplace(line.file, lines);
+ } else {
+ source_lines_seen_pos->second.insert(line.line);
+ }
+ }
+}
+
+bool Disassembler::ElideMixedSourceAndDisassemblyLine(
+ const ExecutionContext &exe_ctx, const SymbolContext &sc,
+ SourceLine &line) {
+
+ // TODO: should we also check target.process.thread.step-avoid-libraries ?
+
+ const RegularExpression *avoid_regex = nullptr;
+
+ // Skip any line #0 entries - they are implementation details
+ if (line.line == 0)
+ return false;
+
+ ThreadSP thread_sp = exe_ctx.GetThreadSP();
+ if (thread_sp) {
+ avoid_regex = thread_sp->GetSymbolsToAvoidRegexp();
+ } else {
+ TargetSP target_sp = exe_ctx.GetTargetSP();
+ if (target_sp) {
+ Status error;
+ OptionValueSP value_sp = target_sp->GetDebugger().GetPropertyValue(
+ &exe_ctx, "target.process.thread.step-avoid-regexp", false, error);
+ if (value_sp && value_sp->GetType() == OptionValue::eTypeRegex) {
+ OptionValueRegex *re = value_sp->GetAsRegex();
+ if (re) {
+ avoid_regex = re->GetCurrentValue();
+ }
+ }
+ }
+ }
+ if (avoid_regex && sc.symbol != nullptr) {
+ const char *function_name =
+ sc.GetFunctionName(Mangled::ePreferDemangledWithoutArguments)
+ .GetCString();
+ if (function_name && avoid_regex->Execute(function_name)) {
+ // skip this source line
+ return true;
+ }
+ }
+ // don't skip this source line
+ return false;
+}
+
+bool Disassembler::PrintInstructions(Disassembler *disasm_ptr,
+ Debugger &debugger, const ArchSpec &arch,
+ const ExecutionContext &exe_ctx,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ // We got some things disassembled...
+ size_t num_instructions_found = disasm_ptr->GetInstructionList().GetSize();
+
+ if (num_instructions > 0 && num_instructions < num_instructions_found)
+ num_instructions_found = num_instructions;
+
+ const uint32_t max_opcode_byte_size =
+ disasm_ptr->GetInstructionList().GetMaxOpcocdeByteSize();
+ SymbolContext sc;
+ SymbolContext prev_sc;
+ AddressRange current_source_line_range;
+ const Address *pc_addr_ptr = nullptr;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+
+ TargetSP target_sp(exe_ctx.GetTargetSP());
+ SourceManager &source_manager =
+ target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager();
+
+ if (frame) {
+ pc_addr_ptr = &frame->GetFrameCodeAddress();
+ }
+ const uint32_t scope =
+ eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol;
+ const bool use_inline_block_range = false;
+
+ const FormatEntity::Entry *disassembly_format = nullptr;
+ FormatEntity::Entry format;
+ if (exe_ctx.HasTargetScope()) {
+ disassembly_format =
+ exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ } else {
+ FormatEntity::Parse("${addr}: ", format);
+ disassembly_format = &format;
+ }
+
+ // First pass: step through the list of instructions, find how long the
+ // initial addresses strings are, insert padding in the second pass so the
+ // opcodes all line up nicely.
+
+ // Also build up the source line mapping if this is mixed source & assembly
+ // mode. Calculate the source line for each assembly instruction (eliding
+ // inlined functions which the user wants to skip).
+
+ std::map<FileSpec, std::set<uint32_t>> source_lines_seen;
+ Symbol *previous_symbol = nullptr;
+
+ size_t address_text_size = 0;
+ for (size_t i = 0; i < num_instructions_found; ++i) {
+ Instruction *inst =
+ disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get();
+ if (inst) {
+ const Address &addr = inst->GetAddress();
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp) {
+ const SymbolContextItem resolve_mask = eSymbolContextFunction |
+ eSymbolContextSymbol |
+ eSymbolContextLineEntry;
+ uint32_t resolved_mask =
+ module_sp->ResolveSymbolContextForAddress(addr, resolve_mask, sc);
+ if (resolved_mask) {
+ StreamString strmstr;
+ Debugger::FormatDisassemblerAddress(disassembly_format, &sc, nullptr,
+ &exe_ctx, &addr, strmstr);
+ size_t cur_line = strmstr.GetSizeOfLastLine();
+ if (cur_line > address_text_size)
+ address_text_size = cur_line;
+
+ // Add entries to our "source_lines_seen" map+set which list which
+ // sources lines occur in this disassembly session. We will print
+ // lines of context around a source line, but we don't want to print
+ // a source line that has a line table entry of its own - we'll leave
+ // that source line to be printed when it actually occurs in the
+ // disassembly.
+
+ if (mixed_source_and_assembly && sc.line_entry.IsValid()) {
+ if (sc.symbol != previous_symbol) {
+ SourceLine decl_line = GetFunctionDeclLineEntry(sc);
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, decl_line))
+ AddLineToSourceLineTables(decl_line, source_lines_seen);
+ }
+ if (sc.line_entry.IsValid()) {
+ SourceLine this_line;
+ this_line.file = sc.line_entry.file;
+ this_line.line = sc.line_entry.line;
+ this_line.column = sc.line_entry.column;
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, this_line))
+ AddLineToSourceLineTables(this_line, source_lines_seen);
+ }
+ }
+ }
+ sc.Clear(false);
+ }
+ }
+ }
+
+ previous_symbol = nullptr;
+ SourceLine previous_line;
+ for (size_t i = 0; i < num_instructions_found; ++i) {
+ Instruction *inst =
+ disasm_ptr->GetInstructionList().GetInstructionAtIndex(i).get();
+
+ if (inst) {
+ const Address &addr = inst->GetAddress();
+ const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr;
+ SourceLinesToDisplay source_lines_to_display;
+
+ prev_sc = sc;
+
+ ModuleSP module_sp(addr.GetModule());
+ if (module_sp) {
+ uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress(
+ addr, eSymbolContextEverything, sc);
+ if (resolved_mask) {
+ if (mixed_source_and_assembly) {
+
+ // If we've started a new function (non-inlined), print all of the
+ // source lines from the function declaration until the first line
+ // table entry - typically the opening curly brace of the function.
+ if (previous_symbol != sc.symbol) {
+ // The default disassembly format puts an extra blank line
+ // between functions - so when we're displaying the source
+ // context for a function, we don't want to add a blank line
+ // after the source context or we'll end up with two of them.
+ if (previous_symbol != nullptr)
+ source_lines_to_display.print_source_context_end_eol = false;
+
+ previous_symbol = sc.symbol;
+ if (sc.function && sc.line_entry.IsValid()) {
+ LineEntry prologue_end_line = sc.line_entry;
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc,
+ prologue_end_line)) {
+ FileSpec func_decl_file;
+ uint32_t func_decl_line;
+ sc.function->GetStartLineSourceInfo(func_decl_file,
+ func_decl_line);
+ if (func_decl_file == prologue_end_line.file ||
+ func_decl_file == prologue_end_line.original_file) {
+ // Add all the lines between the function declaration and
+ // the first non-prologue source line to the list of lines
+ // to print.
+ for (uint32_t lineno = func_decl_line;
+ lineno <= prologue_end_line.line; lineno++) {
+ SourceLine this_line;
+ this_line.file = func_decl_file;
+ this_line.line = lineno;
+ source_lines_to_display.lines.push_back(this_line);
+ }
+ // Mark the last line as the "current" one. Usually this
+ // is the open curly brace.
+ if (source_lines_to_display.lines.size() > 0)
+ source_lines_to_display.current_source_line =
+ source_lines_to_display.lines.size() - 1;
+ }
+ }
+ }
+ sc.GetAddressRange(scope, 0, use_inline_block_range,
+ current_source_line_range);
+ }
+
+ // If we've left a previous source line's address range, print a
+ // new source line
+ if (!current_source_line_range.ContainsFileAddress(addr)) {
+ sc.GetAddressRange(scope, 0, use_inline_block_range,
+ current_source_line_range);
+
+ if (sc != prev_sc && sc.comp_unit && sc.line_entry.IsValid()) {
+ SourceLine this_line;
+ this_line.file = sc.line_entry.file;
+ this_line.line = sc.line_entry.line;
+
+ if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc,
+ this_line)) {
+ // Only print this source line if it is different from the
+ // last source line we printed. There may have been inlined
+ // functions between these lines that we elided, resulting in
+ // the same line being printed twice in a row for a
+ // contiguous block of assembly instructions.
+ if (this_line != previous_line) {
+
+ std::vector<uint32_t> previous_lines;
+ for (uint32_t i = 0;
+ i < num_mixed_context_lines &&
+ (this_line.line - num_mixed_context_lines) > 0;
+ i++) {
+ uint32_t line =
+ this_line.line - num_mixed_context_lines + i;
+ auto pos = source_lines_seen.find(this_line.file);
+ if (pos != source_lines_seen.end()) {
+ if (pos->second.count(line) == 1) {
+ previous_lines.clear();
+ } else {
+ previous_lines.push_back(line);
+ }
+ }
+ }
+ for (size_t i = 0; i < previous_lines.size(); i++) {
+ SourceLine previous_line;
+ previous_line.file = this_line.file;
+ previous_line.line = previous_lines[i];
+ auto pos = source_lines_seen.find(previous_line.file);
+ if (pos != source_lines_seen.end()) {
+ pos->second.insert(previous_line.line);
+ }
+ source_lines_to_display.lines.push_back(previous_line);
+ }
+
+ source_lines_to_display.lines.push_back(this_line);
+ source_lines_to_display.current_source_line =
+ source_lines_to_display.lines.size() - 1;
+
+ for (uint32_t i = 0; i < num_mixed_context_lines; i++) {
+ SourceLine next_line;
+ next_line.file = this_line.file;
+ next_line.line = this_line.line + i + 1;
+ auto pos = source_lines_seen.find(next_line.file);
+ if (pos != source_lines_seen.end()) {
+ if (pos->second.count(next_line.line) == 1)
+ break;
+ pos->second.insert(next_line.line);
+ }
+ source_lines_to_display.lines.push_back(next_line);
+ }
+ }
+ previous_line = this_line;
+ }
+ }
+ }
+ }
+ } else {
+ sc.Clear(true);
+ }
+ }
+
+ if (source_lines_to_display.lines.size() > 0) {
+ strm.EOL();
+ for (size_t idx = 0; idx < source_lines_to_display.lines.size();
+ idx++) {
+ SourceLine ln = source_lines_to_display.lines[idx];
+ const char *line_highlight = "";
+ if (inst_is_at_pc && (options & eOptionMarkPCSourceLine)) {
+ line_highlight = "->";
+ } else if (idx == source_lines_to_display.current_source_line) {
+ line_highlight = "**";
+ }
+ source_manager.DisplaySourceLinesWithLineNumbers(
+ ln.file, ln.line, ln.column, 0, 0, line_highlight, &strm);
+ }
+ if (source_lines_to_display.print_source_context_end_eol)
+ strm.EOL();
+ }
+
+ const bool show_bytes = (options & eOptionShowBytes) != 0;
+ inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc,
+ &prev_sc, nullptr, address_text_size);
+ strm.EOL();
+ } else {
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const ExecutionContext &exe_ctx,
+ uint32_t num_instructions,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
+ AddressRange range;
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ SymbolContext sc(
+ frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));
+ if (sc.function) {
+ range = sc.function->GetAddressRange();
+ } else if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ range.GetBaseAddress() = sc.symbol->GetAddressRef();
+ range.SetByteSize(sc.symbol->GetByteSize());
+ } else {
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ }
+
+ if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0)
+ range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
+ }
+
+ return Disassemble(debugger, arch, plugin_name, flavor, exe_ctx, range,
+ num_instructions, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
+}
+
+Instruction::Instruction(const Address &address, AddressClass addr_class)
+ : m_address(address), m_address_class(addr_class), m_opcode(),
+ m_calculated_strings(false) {}
+
+Instruction::~Instruction() = default;
+
+AddressClass Instruction::GetAddressClass() {
+ if (m_address_class == AddressClass::eInvalid)
+ m_address_class = m_address.GetAddressClass();
+ return m_address_class;
+}
+
+void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
+ bool show_address, bool show_bytes,
+ const ExecutionContext *exe_ctx,
+ const SymbolContext *sym_ctx,
+ const SymbolContext *prev_sym_ctx,
+ const FormatEntity::Entry *disassembly_addr_format,
+ size_t max_address_text_size) {
+ size_t opcode_column_width = 7;
+ const size_t operand_column_width = 25;
+
+ CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx);
+
+ StreamString ss;
+
+ if (show_address) {
+ Debugger::FormatDisassemblerAddress(disassembly_addr_format, sym_ctx,
+ prev_sym_ctx, exe_ctx, &m_address, ss);
+ ss.FillLastLineToColumn(max_address_text_size, ' ');
+ }
+
+ if (show_bytes) {
+ if (m_opcode.GetType() == Opcode::eTypeBytes) {
+ // x86_64 and i386 are the only ones that use bytes right now so pad out
+ // the byte dump to be able to always show 15 bytes (3 chars each) plus a
+ // space
+ if (max_opcode_byte_size > 0)
+ m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1);
+ else
+ m_opcode.Dump(&ss, 15 * 3 + 1);
+ } else {
+ // Else, we have ARM or MIPS which can show up to a uint32_t 0x00000000
+ // (10 spaces) plus two for padding...
+ if (max_opcode_byte_size > 0)
+ m_opcode.Dump(&ss, max_opcode_byte_size * 3 + 1);
+ else
+ m_opcode.Dump(&ss, 12);
+ }
+ }
+
+ const size_t opcode_pos = ss.GetSizeOfLastLine();
+
+ // The default opcode size of 7 characters is plenty for most architectures
+ // but some like arm can pull out the occasional vqrshrun.s16. We won't get
+ // consistent column spacing in these cases, unfortunately.
+ if (m_opcode_name.length() >= opcode_column_width) {
+ opcode_column_width = m_opcode_name.length() + 1;
+ }
+
+ ss.PutCString(m_opcode_name);
+ ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');
+ ss.PutCString(m_mnemonics);
+
+ if (!m_comment.empty()) {
+ ss.FillLastLineToColumn(
+ opcode_pos + opcode_column_width + operand_column_width, ' ');
+ ss.PutCString(" ; ");
+ ss.PutCString(m_comment);
+ }
+ s->PutCString(ss.GetString());
+}
+
+bool Instruction::DumpEmulation(const ArchSpec &arch) {
+ std::unique_ptr<EmulateInstruction> insn_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
+ if (insn_emulator_up) {
+ insn_emulator_up->SetInstruction(GetOpcode(), GetAddress(), nullptr);
+ return insn_emulator_up->EvaluateInstruction(0);
+ }
+
+ return false;
+}
+
+bool Instruction::CanSetBreakpoint () {
+ return !HasDelaySlot();
+}
+
+bool Instruction::HasDelaySlot() {
+ // Default is false.
+ return false;
+}
+
+OptionValueSP Instruction::ReadArray(FILE *in_file, Stream *out_stream,
+ OptionValue::Type data_type) {
+ bool done = false;
+ char buffer[1024];
+
+ auto option_value_sp = std::make_shared<OptionValueArray>(1u << data_type);
+
+ int idx = 0;
+ while (!done) {
+ if (!fgets(buffer, 1023, in_file)) {
+ out_stream->Printf(
+ "Instruction::ReadArray: Error reading file (fgets).\n");
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+
+ std::string line(buffer);
+
+ size_t len = line.size();
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ line.resize(len - 1);
+ }
+
+ if ((line.size() == 1) && line[0] == ']') {
+ done = true;
+ line.clear();
+ }
+
+ if (!line.empty()) {
+ std::string value;
+ static RegularExpression g_reg_exp(
+ llvm::StringRef("^[ \t]*([^ \t]+)[ \t]*$"));
+ llvm::SmallVector<llvm::StringRef, 2> matches;
+ if (g_reg_exp.Execute(line, &matches))
+ value = matches[1].str();
+ else
+ value = line;
+
+ OptionValueSP data_value_sp;
+ switch (data_type) {
+ case OptionValue::eTypeUInt64:
+ data_value_sp = std::make_shared<OptionValueUInt64>(0, 0);
+ data_value_sp->SetValueFromString(value);
+ break;
+ // Other types can be added later as needed.
+ default:
+ data_value_sp = std::make_shared<OptionValueString>(value.c_str(), "");
+ break;
+ }
+
+ option_value_sp->GetAsArray()->InsertValue(idx, data_value_sp);
+ ++idx;
+ }
+ }
+
+ return option_value_sp;
+}
+
+OptionValueSP Instruction::ReadDictionary(FILE *in_file, Stream *out_stream) {
+ bool done = false;
+ char buffer[1024];
+
+ auto option_value_sp = std::make_shared<OptionValueDictionary>();
+ static ConstString encoding_key("data_encoding");
+ OptionValue::Type data_type = OptionValue::eTypeInvalid;
+
+ while (!done) {
+ // Read the next line in the file
+ if (!fgets(buffer, 1023, in_file)) {
+ out_stream->Printf(
+ "Instruction::ReadDictionary: Error reading file (fgets).\n");
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+
+ // Check to see if the line contains the end-of-dictionary marker ("}")
+ std::string line(buffer);
+
+ size_t len = line.size();
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ line.resize(len - 1);
+ }
+
+ if ((line.size() == 1) && (line[0] == '}')) {
+ done = true;
+ line.clear();
+ }
+
+ // Try to find a key-value pair in the current line and add it to the
+ // dictionary.
+ if (!line.empty()) {
+ static RegularExpression g_reg_exp(llvm::StringRef(
+ "^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$"));
+
+ llvm::SmallVector<llvm::StringRef, 3> matches;
+
+ bool reg_exp_success = g_reg_exp.Execute(line, &matches);
+ std::string key;
+ std::string value;
+ if (reg_exp_success) {
+ key = matches[1].str();
+ value = matches[2].str();
+ } else {
+ out_stream->Printf("Instruction::ReadDictionary: Failure executing "
+ "regular expression.\n");
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+
+ ConstString const_key(key.c_str());
+ // Check value to see if it's the start of an array or dictionary.
+
+ lldb::OptionValueSP value_sp;
+ assert(value.empty() == false);
+ assert(key.empty() == false);
+
+ if (value[0] == '{') {
+ assert(value.size() == 1);
+ // value is a dictionary
+ value_sp = ReadDictionary(in_file, out_stream);
+ if (!value_sp) {
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+ } else if (value[0] == '[') {
+ assert(value.size() == 1);
+ // value is an array
+ value_sp = ReadArray(in_file, out_stream, data_type);
+ if (!value_sp) {
+ option_value_sp.reset();
+ return option_value_sp;
+ }
+ // We've used the data_type to read an array; re-set the type to
+ // Invalid
+ data_type = OptionValue::eTypeInvalid;
+ } else if ((value[0] == '0') && (value[1] == 'x')) {
+ value_sp = std::make_shared<OptionValueUInt64>(0, 0);
+ value_sp->SetValueFromString(value);
+ } else {
+ size_t len = value.size();
+ if ((value[0] == '"') && (value[len - 1] == '"'))
+ value = value.substr(1, len - 2);
+ value_sp = std::make_shared<OptionValueString>(value.c_str(), "");
+ }
+
+ if (const_key == encoding_key) {
+ // A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data
+ // indicating the
+ // data type of an upcoming array (usually the next bit of data to be
+ // read in).
+ if (strcmp(value.c_str(), "uint32_t") == 0)
+ data_type = OptionValue::eTypeUInt64;
+ } else
+ option_value_sp->GetAsDictionary()->SetValueForKey(const_key, value_sp,
+ false);
+ }
+ }
+
+ return option_value_sp;
+}
+
+bool Instruction::TestEmulation(Stream *out_stream, const char *file_name) {
+ if (!out_stream)
+ return false;
+
+ if (!file_name) {
+ out_stream->Printf("Instruction::TestEmulation: Missing file_name.");
+ return false;
+ }
+ FILE *test_file = FileSystem::Instance().Fopen(file_name, "r");
+ if (!test_file) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Attempt to open test file failed.");
+ return false;
+ }
+
+ char buffer[256];
+ if (!fgets(buffer, 255, test_file)) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Error reading first line of test file.\n");
+ fclose(test_file);
+ return false;
+ }
+
+ if (strncmp(buffer, "InstructionEmulationState={", 27) != 0) {
+ out_stream->Printf("Instructin::TestEmulation: Test file does not contain "
+ "emulation state dictionary\n");
+ fclose(test_file);
+ return false;
+ }
+
+ // Read all the test information from the test file into an
+ // OptionValueDictionary.
+
+ OptionValueSP data_dictionary_sp(ReadDictionary(test_file, out_stream));
+ if (!data_dictionary_sp) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Error reading Dictionary Object.\n");
+ fclose(test_file);
+ return false;
+ }
+
+ fclose(test_file);
+
+ OptionValueDictionary *data_dictionary =
+ data_dictionary_sp->GetAsDictionary();
+ static ConstString description_key("assembly_string");
+ static ConstString triple_key("triple");
+
+ OptionValueSP value_sp = data_dictionary->GetValueForKey(description_key);
+
+ if (!value_sp) {
+ out_stream->Printf("Instruction::TestEmulation: Test file does not "
+ "contain description string.\n");
+ return false;
+ }
+
+ SetDescription(value_sp->GetStringValue());
+
+ value_sp = data_dictionary->GetValueForKey(triple_key);
+ if (!value_sp) {
+ out_stream->Printf(
+ "Instruction::TestEmulation: Test file does not contain triple.\n");
+ return false;
+ }
+
+ ArchSpec arch;
+ arch.SetTriple(llvm::Triple(value_sp->GetStringValue()));
+
+ bool success = false;
+ std::unique_ptr<EmulateInstruction> insn_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
+ if (insn_emulator_up)
+ success =
+ insn_emulator_up->TestEmulation(out_stream, arch, data_dictionary);
+
+ if (success)
+ out_stream->Printf("Emulation test succeeded.");
+ else
+ out_stream->Printf("Emulation test failed.");
+
+ return success;
+}
+
+bool Instruction::Emulate(
+ const ArchSpec &arch, uint32_t evaluate_options, void *baton,
+ EmulateInstruction::ReadMemoryCallback read_mem_callback,
+ EmulateInstruction::WriteMemoryCallback write_mem_callback,
+ EmulateInstruction::ReadRegisterCallback read_reg_callback,
+ EmulateInstruction::WriteRegisterCallback write_reg_callback) {
+ std::unique_ptr<EmulateInstruction> insn_emulator_up(
+ EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
+ if (insn_emulator_up) {
+ insn_emulator_up->SetBaton(baton);
+ insn_emulator_up->SetCallbacks(read_mem_callback, write_mem_callback,
+ read_reg_callback, write_reg_callback);
+ insn_emulator_up->SetInstruction(GetOpcode(), GetAddress(), nullptr);
+ return insn_emulator_up->EvaluateInstruction(evaluate_options);
+ }
+
+ return false;
+}
+
+uint32_t Instruction::GetData(DataExtractor &data) {
+ return m_opcode.GetData(data);
+}
+
+InstructionList::InstructionList() : m_instructions() {}
+
+InstructionList::~InstructionList() = default;
+
+size_t InstructionList::GetSize() const { return m_instructions.size(); }
+
+uint32_t InstructionList::GetMaxOpcocdeByteSize() const {
+ uint32_t max_inst_size = 0;
+ collection::const_iterator pos, end;
+ for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end;
+ ++pos) {
+ uint32_t inst_size = (*pos)->GetOpcode().GetByteSize();
+ if (max_inst_size < inst_size)
+ max_inst_size = inst_size;
+ }
+ return max_inst_size;
+}
+
+InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const {
+ InstructionSP inst_sp;
+ if (idx < m_instructions.size())
+ inst_sp = m_instructions[idx];
+ return inst_sp;
+}
+
+void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
+ const ExecutionContext *exe_ctx) {
+ const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize();
+ collection::const_iterator pos, begin, end;
+
+ const FormatEntity::Entry *disassembly_format = nullptr;
+ FormatEntity::Entry format;
+ if (exe_ctx && exe_ctx->HasTargetScope()) {
+ disassembly_format =
+ exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ } else {
+ FormatEntity::Parse("${addr}: ", format);
+ disassembly_format = &format;
+ }
+
+ for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin;
+ pos != end; ++pos) {
+ if (pos != begin)
+ s->EOL();
+ (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx,
+ nullptr, nullptr, disassembly_format, 0);
+ }
+}
+
+void InstructionList::Clear() { m_instructions.clear(); }
+
+void InstructionList::Append(lldb::InstructionSP &inst_sp) {
+ if (inst_sp)
+ m_instructions.push_back(inst_sp);
+}
+
+uint32_t
+InstructionList::GetIndexOfNextBranchInstruction(uint32_t start,
+ Target &target,
+ bool ignore_calls) const {
+ size_t num_instructions = m_instructions.size();
+
+ uint32_t next_branch = UINT32_MAX;
+ size_t i;
+ for (i = start; i < num_instructions; i++) {
+ if (m_instructions[i]->DoesBranch()) {
+ if (ignore_calls && m_instructions[i]->IsCall())
+ continue;
+ next_branch = i;
+ break;
+ }
+ }
+
+ // Hexagon needs the first instruction of the packet with the branch. Go
+ // backwards until we find an instruction marked end-of-packet, or until we
+ // hit start.
+ if (target.GetArchitecture().GetTriple().getArch() == llvm::Triple::hexagon) {
+ // If we didn't find a branch, find the last packet start.
+ if (next_branch == UINT32_MAX) {
+ i = num_instructions - 1;
+ }
+
+ while (i > start) {
+ --i;
+
+ Status error;
+ uint32_t inst_bytes;
+ bool prefer_file_cache = false; // Read from process if process is running
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ target.ReadMemory(m_instructions[i]->GetAddress(), prefer_file_cache,
+ &inst_bytes, sizeof(inst_bytes), error, &load_addr);
+ // If we have an error reading memory, return start
+ if (!error.Success())
+ return start;
+ // check if this is the last instruction in a packet bits 15:14 will be
+ // 11b or 00b for a duplex
+ if (((inst_bytes & 0xC000) == 0xC000) ||
+ ((inst_bytes & 0xC000) == 0x0000)) {
+ // instruction after this should be the start of next packet
+ next_branch = i + 1;
+ break;
+ }
+ }
+
+ if (next_branch == UINT32_MAX) {
+ // We couldn't find the previous packet, so return start
+ next_branch = start;
+ }
+ }
+ return next_branch;
+}
+
+uint32_t
+InstructionList::GetIndexOfInstructionAtAddress(const Address &address) {
+ size_t num_instructions = m_instructions.size();
+ uint32_t index = UINT32_MAX;
+ for (size_t i = 0; i < num_instructions; i++) {
+ if (m_instructions[i]->GetAddress() == address) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+}
+
+uint32_t
+InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr,
+ Target &target) {
+ Address address;
+ address.SetLoadAddress(load_addr, &target);
+ return GetIndexOfInstructionAtAddress(address);
+}
+
+size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx,
+ const AddressRange &range,
+ Stream *error_strm_ptr,
+ bool prefer_file_cache) {
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ const addr_t byte_size = range.GetByteSize();
+ if (target == nullptr || byte_size == 0 ||
+ !range.GetBaseAddress().IsValid())
+ return 0;
+
+ auto data_sp = std::make_shared<DataBufferHeap>(byte_size, '\0');
+
+ Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ const size_t bytes_read = target->ReadMemory(
+ range.GetBaseAddress(), prefer_file_cache, data_sp->GetBytes(),
+ data_sp->GetByteSize(), error, &load_addr);
+
+ if (bytes_read > 0) {
+ if (bytes_read != data_sp->GetByteSize())
+ data_sp->SetByteSize(bytes_read);
+ DataExtractor data(data_sp, m_arch.GetByteOrder(),
+ m_arch.GetAddressByteSize());
+ const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
+ return DecodeInstructions(range.GetBaseAddress(), data, 0, UINT32_MAX,
+ false, data_from_file);
+ } else if (error_strm_ptr) {
+ const char *error_cstr = error.AsCString();
+ if (error_cstr) {
+ error_strm_ptr->Printf("error: %s\n", error_cstr);
+ }
+ }
+ } else if (error_strm_ptr) {
+ error_strm_ptr->PutCString("error: invalid execution context\n");
+ }
+ return 0;
+}
+
+size_t Disassembler::ParseInstructions(const ExecutionContext *exe_ctx,
+ const Address &start,
+ uint32_t num_instructions,
+ bool prefer_file_cache) {
+ m_instruction_list.Clear();
+
+ if (exe_ctx == nullptr || num_instructions == 0 || !start.IsValid())
+ return 0;
+
+ Target *target = exe_ctx->GetTargetPtr();
+ // Calculate the max buffer size we will need in order to disassemble
+ const addr_t byte_size = num_instructions * m_arch.GetMaximumOpcodeByteSize();
+
+ if (target == nullptr || byte_size == 0)
+ return 0;
+
+ DataBufferHeap *heap_buffer = new DataBufferHeap(byte_size, '\0');
+ DataBufferSP data_sp(heap_buffer);
+
+ Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ const size_t bytes_read =
+ target->ReadMemory(start, prefer_file_cache, heap_buffer->GetBytes(),
+ byte_size, error, &load_addr);
+
+ const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
+
+ if (bytes_read == 0)
+ return 0;
+ DataExtractor data(data_sp, m_arch.GetByteOrder(),
+ m_arch.GetAddressByteSize());
+
+ const bool append_instructions = true;
+ DecodeInstructions(start, data, 0, num_instructions, append_instructions,
+ data_from_file);
+
+ return m_instruction_list.GetSize();
+}
+
+// Disassembler copy constructor
+Disassembler::Disassembler(const ArchSpec &arch, const char *flavor)
+ : m_arch(arch), m_instruction_list(), m_base_addr(LLDB_INVALID_ADDRESS),
+ m_flavor() {
+ if (flavor == nullptr)
+ m_flavor.assign("default");
+ else
+ m_flavor.assign(flavor);
+
+ // If this is an arm variant that can only include thumb (T16, T32)
+ // instructions, force the arch triple to be "thumbv.." instead of "armv..."
+ if (arch.IsAlwaysThumbInstructions()) {
+ std::string thumb_arch_name(arch.GetTriple().getArchName().str());
+ // Replace "arm" with "thumb" so we get all thumb variants correct
+ if (thumb_arch_name.size() > 3) {
+ thumb_arch_name.erase(0, 3);
+ thumb_arch_name.insert(0, "thumb");
+ }
+ m_arch.SetTriple(thumb_arch_name.c_str());
+ }
+}
+
+Disassembler::~Disassembler() = default;
+
+InstructionList &Disassembler::GetInstructionList() {
+ return m_instruction_list;
+}
+
+const InstructionList &Disassembler::GetInstructionList() const {
+ return m_instruction_list;
+}
+
+// Class PseudoInstruction
+
+PseudoInstruction::PseudoInstruction()
+ : Instruction(Address(), AddressClass::eUnknown), m_description() {}
+
+PseudoInstruction::~PseudoInstruction() = default;
+
+bool PseudoInstruction::DoesBranch() {
+ // This is NOT a valid question for a pseudo instruction.
+ return false;
+}
+
+bool PseudoInstruction::HasDelaySlot() {
+ // This is NOT a valid question for a pseudo instruction.
+ return false;
+}
+
+size_t PseudoInstruction::Decode(const lldb_private::Disassembler &disassembler,
+ const lldb_private::DataExtractor &data,
+ lldb::offset_t data_offset) {
+ return m_opcode.GetByteSize();
+}
+
+void PseudoInstruction::SetOpcode(size_t opcode_size, void *opcode_data) {
+ if (!opcode_data)
+ return;
+
+ switch (opcode_size) {
+ case 8: {
+ uint8_t value8 = *((uint8_t *)opcode_data);
+ m_opcode.SetOpcode8(value8, eByteOrderInvalid);
+ break;
+ }
+ case 16: {
+ uint16_t value16 = *((uint16_t *)opcode_data);
+ m_opcode.SetOpcode16(value16, eByteOrderInvalid);
+ break;
+ }
+ case 32: {
+ uint32_t value32 = *((uint32_t *)opcode_data);
+ m_opcode.SetOpcode32(value32, eByteOrderInvalid);
+ break;
+ }
+ case 64: {
+ uint64_t value64 = *((uint64_t *)opcode_data);
+ m_opcode.SetOpcode64(value64, eByteOrderInvalid);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void PseudoInstruction::SetDescription(llvm::StringRef description) {
+ m_description = description;
+}
+
+Instruction::Operand Instruction::Operand::BuildRegister(ConstString &r) {
+ Operand ret;
+ ret.m_type = Type::Register;
+ ret.m_register = r;
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildImmediate(lldb::addr_t imm,
+ bool neg) {
+ Operand ret;
+ ret.m_type = Type::Immediate;
+ ret.m_immediate = imm;
+ ret.m_negative = neg;
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildImmediate(int64_t imm) {
+ Operand ret;
+ ret.m_type = Type::Immediate;
+ if (imm < 0) {
+ ret.m_immediate = -imm;
+ ret.m_negative = true;
+ } else {
+ ret.m_immediate = imm;
+ ret.m_negative = false;
+ }
+ return ret;
+}
+
+Instruction::Operand
+Instruction::Operand::BuildDereference(const Operand &ref) {
+ Operand ret;
+ ret.m_type = Type::Dereference;
+ ret.m_children = {ref};
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildSum(const Operand &lhs,
+ const Operand &rhs) {
+ Operand ret;
+ ret.m_type = Type::Sum;
+ ret.m_children = {lhs, rhs};
+ return ret;
+}
+
+Instruction::Operand Instruction::Operand::BuildProduct(const Operand &lhs,
+ const Operand &rhs) {
+ Operand ret;
+ ret.m_type = Type::Product;
+ ret.m_children = {lhs, rhs};
+ return ret;
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchBinaryOp(
+ std::function<bool(const Instruction::Operand &)> base,
+ std::function<bool(const Instruction::Operand &)> left,
+ std::function<bool(const Instruction::Operand &)> right) {
+ return [base, left, right](const Instruction::Operand &op) -> bool {
+ return (base(op) && op.m_children.size() == 2 &&
+ ((left(op.m_children[0]) && right(op.m_children[1])) ||
+ (left(op.m_children[1]) && right(op.m_children[0]))));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchUnaryOp(
+ std::function<bool(const Instruction::Operand &)> base,
+ std::function<bool(const Instruction::Operand &)> child) {
+ return [base, child](const Instruction::Operand &op) -> bool {
+ return (base(op) && op.m_children.size() == 1 && child(op.m_children[0]));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchRegOp(const RegisterInfo &info) {
+ return [&info](const Instruction::Operand &op) {
+ return (op.m_type == Instruction::Operand::Type::Register &&
+ (op.m_register == ConstString(info.name) ||
+ op.m_register == ConstString(info.alt_name)));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::FetchRegOp(ConstString &reg) {
+ return [&reg](const Instruction::Operand &op) {
+ if (op.m_type != Instruction::Operand::Type::Register) {
+ return false;
+ }
+ reg = op.m_register;
+ return true;
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchImmOp(int64_t imm) {
+ return [imm](const Instruction::Operand &op) {
+ return (op.m_type == Instruction::Operand::Type::Immediate &&
+ ((op.m_negative && op.m_immediate == (uint64_t)-imm) ||
+ (!op.m_negative && op.m_immediate == (uint64_t)imm)));
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::FetchImmOp(int64_t &imm) {
+ return [&imm](const Instruction::Operand &op) {
+ if (op.m_type != Instruction::Operand::Type::Immediate) {
+ return false;
+ }
+ if (op.m_negative) {
+ imm = -((int64_t)op.m_immediate);
+ } else {
+ imm = ((int64_t)op.m_immediate);
+ }
+ return true;
+ };
+}
+
+std::function<bool(const Instruction::Operand &)>
+lldb_private::OperandMatchers::MatchOpType(Instruction::Operand::Type type) {
+ return [type](const Instruction::Operand &op) { return op.m_type == type; };
+}
diff --git a/contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp b/contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp
new file mode 100644
index 000000000000..12e98de2675c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/DumpDataExtractor.cpp
@@ -0,0 +1,813 @@
+//===-- DumpDataExtractor.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DumpDataExtractor.h"
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-forward.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include <limits>
+#include <memory>
+#include <string>
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include <bitset>
+#include <sstream>
+
+using namespace lldb_private;
+using namespace lldb;
+
+#define NON_PRINTABLE_CHAR '.'
+
+static float half2float(uint16_t half) {
+ union {
+ float f;
+ uint32_t u;
+ } u;
+ int32_t v = (int16_t)half;
+
+ if (0 == (v & 0x7c00)) {
+ u.u = v & 0x80007FFFU;
+ return u.f * ldexpf(1, 125);
+ }
+
+ v <<= 13;
+ u.u = v | 0x70000000U;
+ return u.f * ldexpf(1, -112);
+}
+
+static llvm::Optional<llvm::APInt> GetAPInt(const DataExtractor &data,
+ lldb::offset_t *offset_ptr,
+ lldb::offset_t byte_size) {
+ if (byte_size == 0)
+ return llvm::None;
+
+ llvm::SmallVector<uint64_t, 2> uint64_array;
+ lldb::offset_t bytes_left = byte_size;
+ uint64_t u64;
+ const lldb::ByteOrder byte_order = data.GetByteOrder();
+ if (byte_order == lldb::eByteOrderLittle) {
+ while (bytes_left > 0) {
+ if (bytes_left >= 8) {
+ u64 = data.GetU64(offset_ptr);
+ bytes_left -= 8;
+ } else {
+ u64 = data.GetMaxU64(offset_ptr, (uint32_t)bytes_left);
+ bytes_left = 0;
+ }
+ uint64_array.push_back(u64);
+ }
+ return llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
+ } else if (byte_order == lldb::eByteOrderBig) {
+ lldb::offset_t be_offset = *offset_ptr + byte_size;
+ lldb::offset_t temp_offset;
+ while (bytes_left > 0) {
+ if (bytes_left >= 8) {
+ be_offset -= 8;
+ temp_offset = be_offset;
+ u64 = data.GetU64(&temp_offset);
+ bytes_left -= 8;
+ } else {
+ be_offset -= bytes_left;
+ temp_offset = be_offset;
+ u64 = data.GetMaxU64(&temp_offset, (uint32_t)bytes_left);
+ bytes_left = 0;
+ }
+ uint64_array.push_back(u64);
+ }
+ *offset_ptr += byte_size;
+ return llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array));
+ }
+ return llvm::None;
+}
+
+static lldb::offset_t DumpAPInt(Stream *s, const DataExtractor &data,
+ lldb::offset_t offset, lldb::offset_t byte_size,
+ bool is_signed, unsigned radix) {
+ llvm::Optional<llvm::APInt> apint = GetAPInt(data, &offset, byte_size);
+ if (apint.hasValue()) {
+ std::string apint_str(apint.getValue().toString(radix, is_signed));
+ switch (radix) {
+ case 2:
+ s->Write("0b", 2);
+ break;
+ case 8:
+ s->Write("0", 1);
+ break;
+ case 10:
+ break;
+ }
+ s->Write(apint_str.c_str(), apint_str.size());
+ }
+ return offset;
+}
+
+lldb::offset_t lldb_private::DumpDataExtractor(
+ const DataExtractor &DE, Stream *s, offset_t start_offset,
+ lldb::Format item_format, size_t item_byte_size, size_t item_count,
+ size_t num_per_line, uint64_t base_addr,
+ uint32_t item_bit_size, // If zero, this is not a bitfield value, if
+ // non-zero, the value is a bitfield
+ uint32_t item_bit_offset, // If "item_bit_size" is non-zero, this is the
+ // shift amount to apply to a bitfield
+ ExecutionContextScope *exe_scope) {
+ if (s == nullptr)
+ return start_offset;
+
+ if (item_format == eFormatPointer) {
+ if (item_byte_size != 4 && item_byte_size != 8)
+ item_byte_size = s->GetAddressByteSize();
+ }
+
+ offset_t offset = start_offset;
+
+ if (item_format == eFormatInstruction) {
+ TargetSP target_sp;
+ if (exe_scope)
+ target_sp = exe_scope->CalculateTarget();
+ if (target_sp) {
+ DisassemblerSP disassembler_sp(Disassembler::FindPlugin(
+ target_sp->GetArchitecture(),
+ target_sp->GetDisassemblyFlavor(), nullptr));
+ if (disassembler_sp) {
+ lldb::addr_t addr = base_addr + start_offset;
+ lldb_private::Address so_addr;
+ bool data_from_file = true;
+ if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) {
+ data_from_file = false;
+ } else {
+ if (target_sp->GetSectionLoadList().IsEmpty() ||
+ !target_sp->GetImages().ResolveFileAddress(addr, so_addr))
+ so_addr.SetRawAddress(addr);
+ }
+
+ size_t bytes_consumed = disassembler_sp->DecodeInstructions(
+ so_addr, DE, start_offset, item_count, false, data_from_file);
+
+ if (bytes_consumed) {
+ offset += bytes_consumed;
+ const bool show_address = base_addr != LLDB_INVALID_ADDRESS;
+ const bool show_bytes = true;
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ disassembler_sp->GetInstructionList().Dump(s, show_address,
+ show_bytes, &exe_ctx);
+ }
+ }
+ } else
+ s->Printf("invalid target");
+
+ return offset;
+ }
+
+ if ((item_format == eFormatOSType || item_format == eFormatAddressInfo) &&
+ item_byte_size > 8)
+ item_format = eFormatHex;
+
+ lldb::offset_t line_start_offset = start_offset;
+ for (uint32_t count = 0; DE.ValidOffset(offset) && count < item_count;
+ ++count) {
+ if ((count % num_per_line) == 0) {
+ if (count > 0) {
+ if (item_format == eFormatBytesWithASCII &&
+ offset > line_start_offset) {
+ s->Printf("%*s",
+ static_cast<int>(
+ (num_per_line - (offset - line_start_offset)) * 3 + 2),
+ "");
+ DumpDataExtractor(DE, s, line_start_offset, eFormatCharPrintable, 1,
+ offset - line_start_offset, SIZE_MAX,
+ LLDB_INVALID_ADDRESS, 0, 0);
+ }
+ s->EOL();
+ }
+ if (base_addr != LLDB_INVALID_ADDRESS)
+ s->Printf("0x%8.8" PRIx64 ": ",
+ (uint64_t)(base_addr +
+ (offset - start_offset) / DE.getTargetByteSize()));
+
+ line_start_offset = offset;
+ } else if (item_format != eFormatChar &&
+ item_format != eFormatCharPrintable &&
+ item_format != eFormatCharArray && count > 0) {
+ s->PutChar(' ');
+ }
+
+ switch (item_format) {
+ case eFormatBoolean:
+ if (item_byte_size <= 8)
+ s->Printf("%s", DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset)
+ ? "true"
+ : "false");
+ else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for boolean format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ break;
+
+ case eFormatBinary:
+ if (item_byte_size <= 8) {
+ uint64_t uval64 = DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset);
+ // Avoid std::bitset<64>::to_string() since it is missing in earlier
+ // C++ libraries
+ std::string binary_value(64, '0');
+ std::bitset<64> bits(uval64);
+ for (uint32_t i = 0; i < 64; ++i)
+ if (bits[i])
+ binary_value[64 - 1 - i] = '1';
+ if (item_bit_size > 0)
+ s->Printf("0b%s", binary_value.c_str() + 64 - item_bit_size);
+ else if (item_byte_size > 0 && item_byte_size <= 8)
+ s->Printf("0b%s", binary_value.c_str() + 64 - item_byte_size * 8);
+ } else {
+ const bool is_signed = false;
+ const unsigned radix = 2;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatBytes:
+ case eFormatBytesWithASCII:
+ for (uint32_t i = 0; i < item_byte_size; ++i) {
+ s->Printf("%2.2x", DE.GetU8(&offset));
+ }
+
+ // Put an extra space between the groups of bytes if more than one is
+ // being dumped in a group (item_byte_size is more than 1).
+ if (item_byte_size > 1)
+ s->PutChar(' ');
+ break;
+
+ case eFormatChar:
+ case eFormatCharPrintable:
+ case eFormatCharArray: {
+ // Reject invalid item_byte_size.
+ if (item_byte_size > 8) {
+ s->Printf("error: unsupported byte size (%" PRIu64 ") for char format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+
+ // If we are only printing one character surround it with single quotes
+ if (item_count == 1 && item_format == eFormatChar)
+ s->PutChar('\'');
+
+ const uint64_t ch = DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset);
+ if (isprint(ch))
+ s->Printf("%c", (char)ch);
+ else if (item_format != eFormatCharPrintable) {
+ switch (ch) {
+ case '\033':
+ s->Printf("\\e");
+ break;
+ case '\a':
+ s->Printf("\\a");
+ break;
+ case '\b':
+ s->Printf("\\b");
+ break;
+ case '\f':
+ s->Printf("\\f");
+ break;
+ case '\n':
+ s->Printf("\\n");
+ break;
+ case '\r':
+ s->Printf("\\r");
+ break;
+ case '\t':
+ s->Printf("\\t");
+ break;
+ case '\v':
+ s->Printf("\\v");
+ break;
+ case '\0':
+ s->Printf("\\0");
+ break;
+ default:
+ if (item_byte_size == 1)
+ s->Printf("\\x%2.2x", (uint8_t)ch);
+ else
+ s->Printf("%" PRIu64, ch);
+ break;
+ }
+ } else {
+ s->PutChar(NON_PRINTABLE_CHAR);
+ }
+
+ // If we are only printing one character surround it with single quotes
+ if (item_count == 1 && item_format == eFormatChar)
+ s->PutChar('\'');
+ } break;
+
+ case eFormatEnum: // Print enum value as a signed integer when we don't get
+ // the enum type
+ case eFormatDecimal:
+ if (item_byte_size <= 8)
+ s->Printf("%" PRId64,
+ DE.GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ else {
+ const bool is_signed = true;
+ const unsigned radix = 10;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatUnsigned:
+ if (item_byte_size <= 8)
+ s->Printf("%" PRIu64,
+ DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ else {
+ const bool is_signed = false;
+ const unsigned radix = 10;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatOctal:
+ if (item_byte_size <= 8)
+ s->Printf("0%" PRIo64,
+ DE.GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ else {
+ const bool is_signed = false;
+ const unsigned radix = 8;
+ offset = DumpAPInt(s, DE, offset, item_byte_size, is_signed, radix);
+ }
+ break;
+
+ case eFormatOSType: {
+ uint64_t uval64 = DE.GetMaxU64Bitfield(&offset, item_byte_size,
+ item_bit_size, item_bit_offset);
+ s->PutChar('\'');
+ for (uint32_t i = 0; i < item_byte_size; ++i) {
+ uint8_t ch = (uint8_t)(uval64 >> ((item_byte_size - i - 1) * 8));
+ if (isprint(ch))
+ s->Printf("%c", ch);
+ else {
+ switch (ch) {
+ case '\033':
+ s->Printf("\\e");
+ break;
+ case '\a':
+ s->Printf("\\a");
+ break;
+ case '\b':
+ s->Printf("\\b");
+ break;
+ case '\f':
+ s->Printf("\\f");
+ break;
+ case '\n':
+ s->Printf("\\n");
+ break;
+ case '\r':
+ s->Printf("\\r");
+ break;
+ case '\t':
+ s->Printf("\\t");
+ break;
+ case '\v':
+ s->Printf("\\v");
+ break;
+ case '\0':
+ s->Printf("\\0");
+ break;
+ default:
+ s->Printf("\\x%2.2x", ch);
+ break;
+ }
+ }
+ }
+ s->PutChar('\'');
+ } break;
+
+ case eFormatCString: {
+ const char *cstr = DE.GetCStr(&offset);
+
+ if (!cstr) {
+ s->Printf("NULL");
+ offset = LLDB_INVALID_OFFSET;
+ } else {
+ s->PutChar('\"');
+
+ while (const char c = *cstr) {
+ if (isprint(c)) {
+ s->PutChar(c);
+ } else {
+ switch (c) {
+ case '\033':
+ s->Printf("\\e");
+ break;
+ case '\a':
+ s->Printf("\\a");
+ break;
+ case '\b':
+ s->Printf("\\b");
+ break;
+ case '\f':
+ s->Printf("\\f");
+ break;
+ case '\n':
+ s->Printf("\\n");
+ break;
+ case '\r':
+ s->Printf("\\r");
+ break;
+ case '\t':
+ s->Printf("\\t");
+ break;
+ case '\v':
+ s->Printf("\\v");
+ break;
+ default:
+ s->Printf("\\x%2.2x", c);
+ break;
+ }
+ }
+
+ ++cstr;
+ }
+
+ s->PutChar('\"');
+ }
+ } break;
+
+ case eFormatPointer:
+ s->Address(DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset),
+ sizeof(addr_t));
+ break;
+
+ case eFormatComplexInteger: {
+ size_t complex_int_byte_size = item_byte_size / 2;
+
+ if (complex_int_byte_size > 0 && complex_int_byte_size <= 8) {
+ s->Printf("%" PRIu64,
+ DE.GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0));
+ s->Printf(" + %" PRIu64 "i",
+ DE.GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0));
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for complex integer format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ } break;
+
+ case eFormatComplex:
+ if (sizeof(float) * 2 == item_byte_size) {
+ float f32_1 = DE.GetFloat(&offset);
+ float f32_2 = DE.GetFloat(&offset);
+
+ s->Printf("%g + %gi", f32_1, f32_2);
+ break;
+ } else if (sizeof(double) * 2 == item_byte_size) {
+ double d64_1 = DE.GetDouble(&offset);
+ double d64_2 = DE.GetDouble(&offset);
+
+ s->Printf("%lg + %lgi", d64_1, d64_2);
+ break;
+ } else if (sizeof(long double) * 2 == item_byte_size) {
+ long double ld64_1 = DE.GetLongDouble(&offset);
+ long double ld64_2 = DE.GetLongDouble(&offset);
+ s->Printf("%Lg + %Lgi", ld64_1, ld64_2);
+ break;
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for complex float format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ break;
+
+ default:
+ case eFormatDefault:
+ case eFormatHex:
+ case eFormatHexUppercase: {
+ bool wantsuppercase = (item_format == eFormatHexUppercase);
+ switch (item_byte_size) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64,
+ (int)(2 * item_byte_size), (int)(2 * item_byte_size),
+ DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset));
+ break;
+ default: {
+ assert(item_bit_size == 0 && item_bit_offset == 0);
+ const uint8_t *bytes =
+ (const uint8_t *)DE.GetData(&offset, item_byte_size);
+ if (bytes) {
+ s->PutCString("0x");
+ uint32_t idx;
+ if (DE.GetByteOrder() == eByteOrderBig) {
+ for (idx = 0; idx < item_byte_size; ++idx)
+ s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[idx]);
+ } else {
+ for (idx = 0; idx < item_byte_size; ++idx)
+ s->Printf(wantsuppercase ? "%2.2X" : "%2.2x",
+ bytes[item_byte_size - 1 - idx]);
+ }
+ }
+ } break;
+ }
+ } break;
+
+ case eFormatFloat: {
+ TargetSP target_sp;
+ bool used_upfloat = false;
+ if (exe_scope)
+ target_sp = exe_scope->CalculateTarget();
+ if (target_sp) {
+ auto type_system_or_err =
+ target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC);
+ if (!type_system_or_err) {
+ llvm::consumeError(type_system_or_err.takeError());
+ } else {
+ auto &type_system = *type_system_or_err;
+ llvm::SmallVector<char, 256> sv;
+ // Show full precision when printing float values
+ const unsigned format_precision = 0;
+ const unsigned format_max_padding =
+ target_sp->GetMaxZeroPaddingInFloatFormat();
+
+ const auto &semantics =
+ type_system.GetFloatTypeSemantics(item_byte_size);
+
+ // Recalculate the byte size in case of a difference. This is possible
+ // when item_byte_size is 16 (128-bit), because you could get back the
+ // x87DoubleExtended semantics which has a byte size of 10 (80-bit).
+ const size_t semantics_byte_size =
+ (llvm::APFloat::getSizeInBits(semantics) + 7) / 8;
+ llvm::Optional<llvm::APInt> apint =
+ GetAPInt(DE, &offset, semantics_byte_size);
+ if (apint.hasValue()) {
+ llvm::APFloat apfloat(semantics, apint.getValue());
+ apfloat.toString(sv, format_precision, format_max_padding);
+ if (!sv.empty()) {
+ s->Printf("%*.*s", (int)sv.size(), (int)sv.size(), sv.data());
+ used_upfloat = true;
+ }
+ }
+ }
+ }
+
+ if (!used_upfloat) {
+ std::ostringstream ss;
+ if (item_byte_size == sizeof(float) || item_byte_size == 2) {
+ float f;
+ if (item_byte_size == 2) {
+ uint16_t half = DE.GetU16(&offset);
+ f = half2float(half);
+ } else {
+ f = DE.GetFloat(&offset);
+ }
+ ss.precision(std::numeric_limits<float>::digits10);
+ ss << f;
+ } else if (item_byte_size == sizeof(double)) {
+ ss.precision(std::numeric_limits<double>::digits10);
+ ss << DE.GetDouble(&offset);
+ } else if (item_byte_size == sizeof(long double) ||
+ item_byte_size == 10) {
+ ss.precision(std::numeric_limits<long double>::digits10);
+ ss << DE.GetLongDouble(&offset);
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for float format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ ss.flush();
+ s->Printf("%s", ss.str().c_str());
+ }
+ } break;
+
+ case eFormatUnicode16:
+ s->Printf("U+%4.4x", DE.GetU16(&offset));
+ break;
+
+ case eFormatUnicode32:
+ s->Printf("U+0x%8.8x", DE.GetU32(&offset));
+ break;
+
+ case eFormatAddressInfo: {
+ addr_t addr = DE.GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size,
+ item_bit_offset);
+ s->Printf("0x%*.*" PRIx64, (int)(2 * item_byte_size),
+ (int)(2 * item_byte_size), addr);
+ if (exe_scope) {
+ TargetSP target_sp(exe_scope->CalculateTarget());
+ lldb_private::Address so_addr;
+ if (target_sp) {
+ if (target_sp->GetSectionLoadList().ResolveLoadAddress(addr,
+ so_addr)) {
+ s->PutChar(' ');
+ so_addr.Dump(s, exe_scope, Address::DumpStyleResolvedDescription,
+ Address::DumpStyleModuleWithFileAddress);
+ } else {
+ so_addr.SetOffset(addr);
+ so_addr.Dump(s, exe_scope,
+ Address::DumpStyleResolvedPointerDescription);
+ }
+ }
+ }
+ } break;
+
+ case eFormatHexFloat:
+ if (sizeof(float) == item_byte_size) {
+ char float_cstr[256];
+ llvm::APFloat ap_float(DE.GetFloat(&offset));
+ ap_float.convertToHexString(float_cstr, 0, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ s->Printf("%s", float_cstr);
+ break;
+ } else if (sizeof(double) == item_byte_size) {
+ char float_cstr[256];
+ llvm::APFloat ap_float(DE.GetDouble(&offset));
+ ap_float.convertToHexString(float_cstr, 0, false,
+ llvm::APFloat::rmNearestTiesToEven);
+ s->Printf("%s", float_cstr);
+ break;
+ } else {
+ s->Printf("error: unsupported byte size (%" PRIu64
+ ") for hex float format",
+ (uint64_t)item_byte_size);
+ return offset;
+ }
+ break;
+
+ // please keep the single-item formats below in sync with
+ // FormatManager::GetSingleItemFormat if you fail to do so, users will
+ // start getting different outputs depending on internal implementation
+ // details they should not care about ||
+ case eFormatVectorOfChar: // ||
+ s->PutChar('{'); // \/
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatCharArray, 1, item_byte_size,
+ item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt8:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatDecimal, 1, item_byte_size,
+ item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt8:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, 1, item_byte_size,
+ item_byte_size, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt16:
+ s->PutChar('{');
+ offset = DumpDataExtractor(
+ DE, s, offset, eFormatDecimal, sizeof(uint16_t),
+ item_byte_size / sizeof(uint16_t), item_byte_size / sizeof(uint16_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt16:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint16_t),
+ item_byte_size / sizeof(uint16_t),
+ item_byte_size / sizeof(uint16_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt32:
+ s->PutChar('{');
+ offset = DumpDataExtractor(
+ DE, s, offset, eFormatDecimal, sizeof(uint32_t),
+ item_byte_size / sizeof(uint32_t), item_byte_size / sizeof(uint32_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt32:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint32_t),
+ item_byte_size / sizeof(uint32_t),
+ item_byte_size / sizeof(uint32_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfSInt64:
+ s->PutChar('{');
+ offset = DumpDataExtractor(
+ DE, s, offset, eFormatDecimal, sizeof(uint64_t),
+ item_byte_size / sizeof(uint64_t), item_byte_size / sizeof(uint64_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt64:
+ s->PutChar('{');
+ offset = DumpDataExtractor(DE, s, offset, eFormatHex, sizeof(uint64_t),
+ item_byte_size / sizeof(uint64_t),
+ item_byte_size / sizeof(uint64_t),
+ LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfFloat16:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatFloat, 2, item_byte_size / 2,
+ item_byte_size / 2, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfFloat32:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatFloat, 4, item_byte_size / 4,
+ item_byte_size / 4, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfFloat64:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatFloat, 8, item_byte_size / 8,
+ item_byte_size / 8, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+
+ case eFormatVectorOfUInt128:
+ s->PutChar('{');
+ offset =
+ DumpDataExtractor(DE, s, offset, eFormatHex, 16, item_byte_size / 16,
+ item_byte_size / 16, LLDB_INVALID_ADDRESS, 0, 0);
+ s->PutChar('}');
+ break;
+ }
+ }
+
+ if (item_format == eFormatBytesWithASCII && offset > line_start_offset) {
+ s->Printf("%*s", static_cast<int>(
+ (num_per_line - (offset - line_start_offset)) * 3 + 2),
+ "");
+ DumpDataExtractor(DE, s, line_start_offset, eFormatCharPrintable, 1,
+ offset - line_start_offset, SIZE_MAX,
+ LLDB_INVALID_ADDRESS, 0, 0);
+ }
+ return offset; // Return the offset at which we ended up
+}
+
+void lldb_private::DumpHexBytes(Stream *s, const void *src, size_t src_len,
+ uint32_t bytes_per_line,
+ lldb::addr_t base_addr) {
+ DataExtractor data(src, src_len, lldb::eByteOrderLittle, 4);
+ DumpDataExtractor(data, s,
+ 0, // Offset into "src"
+ lldb::eFormatBytes, // Dump as hex bytes
+ 1, // Size of each item is 1 for single bytes
+ src_len, // Number of bytes
+ bytes_per_line, // Num bytes per line
+ base_addr, // Base address
+ 0, 0); // Bitfield info
+}
diff --git a/contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp b/contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp
new file mode 100644
index 000000000000..74b02413f101
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/DumpRegisterValue.cpp
@@ -0,0 +1,78 @@
+//===-- DumpRegisterValue.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/DumpDataExtractor.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-private-types.h"
+
+using namespace lldb;
+
+bool lldb_private::DumpRegisterValue(const RegisterValue &reg_val, Stream *s,
+ const RegisterInfo *reg_info,
+ bool prefix_with_name,
+ bool prefix_with_alt_name, Format format,
+ uint32_t reg_name_right_align_at) {
+ DataExtractor data;
+ if (reg_val.GetData(data)) {
+ bool name_printed = false;
+ // For simplicity, alignment of the register name printing applies only in
+ // the most common case where:
+ //
+ // prefix_with_name^prefix_with_alt_name is true
+ //
+ StreamString format_string;
+ if (reg_name_right_align_at && (prefix_with_name ^ prefix_with_alt_name))
+ format_string.Printf("%%%us", reg_name_right_align_at);
+ else
+ format_string.Printf("%%s");
+ std::string fmt = format_string.GetString();
+ if (prefix_with_name) {
+ if (reg_info->name) {
+ s->Printf(fmt.c_str(), reg_info->name);
+ name_printed = true;
+ } else if (reg_info->alt_name) {
+ s->Printf(fmt.c_str(), reg_info->alt_name);
+ prefix_with_alt_name = false;
+ name_printed = true;
+ }
+ }
+ if (prefix_with_alt_name) {
+ if (name_printed)
+ s->PutChar('/');
+ if (reg_info->alt_name) {
+ s->Printf(fmt.c_str(), reg_info->alt_name);
+ name_printed = true;
+ } else if (!name_printed) {
+ // No alternate name but we were asked to display a name, so show the
+ // main name
+ s->Printf(fmt.c_str(), reg_info->name);
+ name_printed = true;
+ }
+ }
+ if (name_printed)
+ s->PutCString(" = ");
+
+ if (format == eFormatDefault)
+ format = reg_info->format;
+
+ DumpDataExtractor(data, s,
+ 0, // Offset in "data"
+ format, // Format to use when dumping
+ reg_info->byte_size, // item_byte_size
+ 1, // item_count
+ UINT32_MAX, // num_per_line
+ LLDB_INVALID_ADDRESS, // base_addr
+ 0, // item_bit_size
+ 0); // item_bit_offset
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp b/contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp
new file mode 100644
index 000000000000..57130d6fa57a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/DynamicLoader.cpp
@@ -0,0 +1,245 @@
+//===-- DynamicLoader.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/DynamicLoader.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-private-interfaces.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <memory>
+
+#include <assert.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+DynamicLoader *DynamicLoader::FindPlugin(Process *process,
+ const char *plugin_name) {
+ DynamicLoaderCreateInstance create_callback = nullptr;
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback =
+ PluginManager::GetDynamicLoaderCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ std::unique_ptr<DynamicLoader> instance_up(
+ create_callback(process, true));
+ if (instance_up)
+ return instance_up.release();
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ std::unique_ptr<DynamicLoader> instance_up(
+ create_callback(process, false));
+ if (instance_up)
+ return instance_up.release();
+ }
+ }
+ return nullptr;
+}
+
+DynamicLoader::DynamicLoader(Process *process) : m_process(process) {}
+
+DynamicLoader::~DynamicLoader() = default;
+
+// Accessosors to the global setting as to whether to stop at image (shared
+// library) loading/unloading.
+
+bool DynamicLoader::GetStopWhenImagesChange() const {
+ return m_process->GetStopOnSharedLibraryEvents();
+}
+
+void DynamicLoader::SetStopWhenImagesChange(bool stop) {
+ m_process->SetStopOnSharedLibraryEvents(stop);
+}
+
+ModuleSP DynamicLoader::GetTargetExecutable() {
+ Target &target = m_process->GetTarget();
+ ModuleSP executable = target.GetExecutableModule();
+
+ if (executable) {
+ if (FileSystem::Instance().Exists(executable->GetFileSpec())) {
+ ModuleSpec module_spec(executable->GetFileSpec(),
+ executable->GetArchitecture());
+ auto module_sp = std::make_shared<Module>(module_spec);
+
+ // Check if the executable has changed and set it to the target
+ // executable if they differ.
+ if (module_sp && module_sp->GetUUID().IsValid() &&
+ executable->GetUUID().IsValid()) {
+ if (module_sp->GetUUID() != executable->GetUUID())
+ executable.reset();
+ } else if (executable->FileHasChanged()) {
+ executable.reset();
+ }
+
+ if (!executable) {
+ executable = target.GetOrCreateModule(module_spec, true /* notify */);
+ if (executable.get() != target.GetExecutableModulePointer()) {
+ // Don't load dependent images since we are in dyld where we will
+ // know and find out about all images that are loaded
+ target.SetExecutableModule(executable, eLoadDependentsNo);
+ }
+ }
+ }
+ }
+ return executable;
+}
+
+void DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset);
+}
+
+void DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ bool changed;
+ module->SetLoadAddress(m_process->GetTarget(), base_addr, base_addr_is_offset,
+ changed);
+}
+
+void DynamicLoader::UnloadSections(const ModuleSP module) {
+ UnloadSectionsCommon(module);
+}
+
+void DynamicLoader::UnloadSectionsCommon(const ModuleSP module) {
+ Target &target = m_process->GetTarget();
+ const SectionList *sections = GetSectionListFromModule(module);
+
+ assert(sections && "SectionList missing from unloaded module.");
+
+ const size_t num_sections = sections->GetSize();
+ for (size_t i = 0; i < num_sections; ++i) {
+ SectionSP section_sp(sections->GetSectionAtIndex(i));
+ target.SetSectionUnloaded(section_sp);
+ }
+}
+
+const SectionList *
+DynamicLoader::GetSectionListFromModule(const ModuleSP module) const {
+ SectionList *sections = nullptr;
+ if (module) {
+ ObjectFile *obj_file = module->GetObjectFile();
+ if (obj_file != nullptr) {
+ sections = obj_file->GetSectionList();
+ }
+ }
+ return sections;
+}
+
+ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file,
+ addr_t link_map_addr,
+ addr_t base_addr,
+ bool base_addr_is_offset) {
+ Target &target = m_process->GetTarget();
+ ModuleList &modules = target.GetImages();
+ ModuleSpec module_spec(file, target.GetArchitecture());
+ ModuleSP module_sp;
+
+ if ((module_sp = modules.FindFirstModule(module_spec))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr,
+ base_addr_is_offset);
+ return module_sp;
+ }
+
+ if ((module_sp = target.GetOrCreateModule(module_spec,
+ true /* notify */))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr,
+ base_addr_is_offset);
+ return module_sp;
+ }
+
+ bool check_alternative_file_name = true;
+ if (base_addr_is_offset) {
+ // Try to fetch the load address of the file from the process as we need
+ // absolute load address to read the file out of the memory instead of a
+ // load bias.
+ bool is_loaded = false;
+ lldb::addr_t load_addr;
+ Status error = m_process->GetFileLoadAddress(file, is_loaded, load_addr);
+ if (error.Success() && is_loaded) {
+ check_alternative_file_name = false;
+ base_addr = load_addr;
+ }
+ }
+
+ // We failed to find the module based on its name. Lets try to check if we
+ // can find a different name based on the memory region info.
+ if (check_alternative_file_name) {
+ MemoryRegionInfo memory_info;
+ Status error = m_process->GetMemoryRegionInfo(base_addr, memory_info);
+ if (error.Success() && memory_info.GetMapped() &&
+ memory_info.GetRange().GetRangeBase() == base_addr &&
+ !(memory_info.GetName().IsEmpty())) {
+ ModuleSpec new_module_spec(FileSpec(memory_info.GetName().AsCString()),
+ target.GetArchitecture());
+
+ if ((module_sp = modules.FindFirstModule(new_module_spec))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr, false);
+ return module_sp;
+ }
+
+ if ((module_sp = target.GetOrCreateModule(new_module_spec,
+ true /* notify */))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr, false);
+ return module_sp;
+ }
+ }
+ }
+
+ if ((module_sp = m_process->ReadModuleFromMemory(file, base_addr))) {
+ UpdateLoadedSections(module_sp, link_map_addr, base_addr, false);
+ target.GetImages().AppendIfNeeded(module_sp);
+ }
+
+ return module_sp;
+}
+
+int64_t DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr,
+ int size_in_bytes) {
+ Status error;
+ uint64_t value =
+ m_process->ReadUnsignedIntegerFromMemory(addr, size_in_bytes, 0, error);
+ if (error.Fail())
+ return -1;
+ else
+ return (int64_t)value;
+}
+
+addr_t DynamicLoader::ReadPointer(addr_t addr) {
+ Status error;
+ addr_t value = m_process->ReadPointerFromMemory(addr, error);
+ if (error.Fail())
+ return LLDB_INVALID_ADDRESS;
+ else
+ return value;
+}
+
+void DynamicLoader::LoadOperatingSystemPlugin(bool flush)
+{
+ if (m_process)
+ m_process->LoadOperatingSystemPlugin(flush);
+}
+
diff --git a/contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp b/contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp
new file mode 100644
index 000000000000..62942fb715b3
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/EmulateInstruction.cpp
@@ -0,0 +1,585 @@
+//===-- EmulateInstruction.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/EmulateInstruction.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-interfaces.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <cstring>
+#include <memory>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+namespace lldb_private {
+class Target;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+EmulateInstruction *
+EmulateInstruction::FindPlugin(const ArchSpec &arch,
+ InstructionType supported_inst_type,
+ const char *plugin_name) {
+ EmulateInstructionCreateInstance create_callback = nullptr;
+ if (plugin_name) {
+ ConstString const_plugin_name(plugin_name);
+ create_callback =
+ PluginManager::GetEmulateInstructionCreateCallbackForPluginName(
+ const_plugin_name);
+ if (create_callback) {
+ EmulateInstruction *emulate_insn_ptr =
+ create_callback(arch, supported_inst_type);
+ if (emulate_insn_ptr)
+ return emulate_insn_ptr;
+ }
+ } else {
+ for (uint32_t idx = 0;
+ (create_callback =
+ PluginManager::GetEmulateInstructionCreateCallbackAtIndex(idx)) !=
+ nullptr;
+ ++idx) {
+ EmulateInstruction *emulate_insn_ptr =
+ create_callback(arch, supported_inst_type);
+ if (emulate_insn_ptr)
+ return emulate_insn_ptr;
+ }
+ }
+ return nullptr;
+}
+
+EmulateInstruction::EmulateInstruction(const ArchSpec &arch) : m_arch(arch) {}
+
+bool EmulateInstruction::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ if (m_read_reg_callback != nullptr)
+ return m_read_reg_callback(this, m_baton, reg_info, reg_value);
+ return false;
+}
+
+bool EmulateInstruction::ReadRegister(lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ RegisterValue &reg_value) {
+ RegisterInfo reg_info;
+ if (GetRegisterInfo(reg_kind, reg_num, reg_info))
+ return ReadRegister(&reg_info, reg_value);
+ return false;
+}
+
+uint64_t EmulateInstruction::ReadRegisterUnsigned(lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ uint64_t fail_value,
+ bool *success_ptr) {
+ RegisterValue reg_value;
+ if (ReadRegister(reg_kind, reg_num, reg_value))
+ return reg_value.GetAsUInt64(fail_value, success_ptr);
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+uint64_t EmulateInstruction::ReadRegisterUnsigned(const RegisterInfo *reg_info,
+ uint64_t fail_value,
+ bool *success_ptr) {
+ RegisterValue reg_value;
+ if (ReadRegister(reg_info, reg_value))
+ return reg_value.GetAsUInt64(fail_value, success_ptr);
+ if (success_ptr)
+ *success_ptr = false;
+ return fail_value;
+}
+
+bool EmulateInstruction::WriteRegister(const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (m_write_reg_callback != nullptr)
+ return m_write_reg_callback(this, m_baton, context, reg_info, reg_value);
+ return false;
+}
+
+bool EmulateInstruction::WriteRegister(const Context &context,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ const RegisterValue &reg_value) {
+ RegisterInfo reg_info;
+ if (GetRegisterInfo(reg_kind, reg_num, reg_info))
+ return WriteRegister(context, &reg_info, reg_value);
+ return false;
+}
+
+bool EmulateInstruction::WriteRegisterUnsigned(const Context &context,
+ lldb::RegisterKind reg_kind,
+ uint32_t reg_num,
+ uint64_t uint_value) {
+ RegisterInfo reg_info;
+ if (GetRegisterInfo(reg_kind, reg_num, reg_info)) {
+ RegisterValue reg_value;
+ if (reg_value.SetUInt(uint_value, reg_info.byte_size))
+ return WriteRegister(context, &reg_info, reg_value);
+ }
+ return false;
+}
+
+bool EmulateInstruction::WriteRegisterUnsigned(const Context &context,
+ const RegisterInfo *reg_info,
+ uint64_t uint_value) {
+ if (reg_info != nullptr) {
+ RegisterValue reg_value;
+ if (reg_value.SetUInt(uint_value, reg_info->byte_size))
+ return WriteRegister(context, reg_info, reg_value);
+ }
+ return false;
+}
+
+size_t EmulateInstruction::ReadMemory(const Context &context, lldb::addr_t addr,
+ void *dst, size_t dst_len) {
+ if (m_read_mem_callback != nullptr)
+ return m_read_mem_callback(this, m_baton, context, addr, dst, dst_len) ==
+ dst_len;
+ return false;
+}
+
+uint64_t EmulateInstruction::ReadMemoryUnsigned(const Context &context,
+ lldb::addr_t addr,
+ size_t byte_size,
+ uint64_t fail_value,
+ bool *success_ptr) {
+ uint64_t uval64 = 0;
+ bool success = false;
+ if (byte_size <= 8) {
+ uint8_t buf[sizeof(uint64_t)];
+ size_t bytes_read =
+ m_read_mem_callback(this, m_baton, context, addr, buf, byte_size);
+ if (bytes_read == byte_size) {
+ lldb::offset_t offset = 0;
+ DataExtractor data(buf, byte_size, GetByteOrder(), GetAddressByteSize());
+ uval64 = data.GetMaxU64(&offset, byte_size);
+ success = true;
+ }
+ }
+
+ if (success_ptr)
+ *success_ptr = success;
+
+ if (!success)
+ uval64 = fail_value;
+ return uval64;
+}
+
+bool EmulateInstruction::WriteMemoryUnsigned(const Context &context,
+ lldb::addr_t addr, uint64_t uval,
+ size_t uval_byte_size) {
+ StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder());
+ strm.PutMaxHex64(uval, uval_byte_size);
+
+ size_t bytes_written = m_write_mem_callback(
+ this, m_baton, context, addr, strm.GetString().data(), uval_byte_size);
+ return (bytes_written == uval_byte_size);
+}
+
+bool EmulateInstruction::WriteMemory(const Context &context, lldb::addr_t addr,
+ const void *src, size_t src_len) {
+ if (m_write_mem_callback != nullptr)
+ return m_write_mem_callback(this, m_baton, context, addr, src, src_len) ==
+ src_len;
+ return false;
+}
+
+void EmulateInstruction::SetBaton(void *baton) { m_baton = baton; }
+
+void EmulateInstruction::SetCallbacks(
+ ReadMemoryCallback read_mem_callback,
+ WriteMemoryCallback write_mem_callback,
+ ReadRegisterCallback read_reg_callback,
+ WriteRegisterCallback write_reg_callback) {
+ m_read_mem_callback = read_mem_callback;
+ m_write_mem_callback = write_mem_callback;
+ m_read_reg_callback = read_reg_callback;
+ m_write_reg_callback = write_reg_callback;
+}
+
+void EmulateInstruction::SetReadMemCallback(
+ ReadMemoryCallback read_mem_callback) {
+ m_read_mem_callback = read_mem_callback;
+}
+
+void EmulateInstruction::SetWriteMemCallback(
+ WriteMemoryCallback write_mem_callback) {
+ m_write_mem_callback = write_mem_callback;
+}
+
+void EmulateInstruction::SetReadRegCallback(
+ ReadRegisterCallback read_reg_callback) {
+ m_read_reg_callback = read_reg_callback;
+}
+
+void EmulateInstruction::SetWriteRegCallback(
+ WriteRegisterCallback write_reg_callback) {
+ m_write_reg_callback = write_reg_callback;
+}
+
+//
+// Read & Write Memory and Registers callback functions.
+//
+
+size_t EmulateInstruction::ReadMemoryFrame(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ lldb::addr_t addr, void *dst,
+ size_t dst_len) {
+ if (baton == nullptr || dst == nullptr || dst_len == 0)
+ return 0;
+
+ StackFrame *frame = (StackFrame *)baton;
+
+ ProcessSP process_sp(frame->CalculateProcess());
+ if (process_sp) {
+ Status error;
+ return process_sp->ReadMemory(addr, dst, dst_len, error);
+ }
+ return 0;
+}
+
+size_t EmulateInstruction::WriteMemoryFrame(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ lldb::addr_t addr, const void *src,
+ size_t src_len) {
+ if (baton == nullptr || src == nullptr || src_len == 0)
+ return 0;
+
+ StackFrame *frame = (StackFrame *)baton;
+
+ ProcessSP process_sp(frame->CalculateProcess());
+ if (process_sp) {
+ Status error;
+ return process_sp->WriteMemory(addr, src, src_len, error);
+ }
+
+ return 0;
+}
+
+bool EmulateInstruction::ReadRegisterFrame(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ if (baton == nullptr)
+ return false;
+
+ StackFrame *frame = (StackFrame *)baton;
+ return frame->GetRegisterContext()->ReadRegister(reg_info, reg_value);
+}
+
+bool EmulateInstruction::WriteRegisterFrame(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ if (baton == nullptr)
+ return false;
+
+ StackFrame *frame = (StackFrame *)baton;
+ return frame->GetRegisterContext()->WriteRegister(reg_info, reg_value);
+}
+
+size_t EmulateInstruction::ReadMemoryDefault(EmulateInstruction *instruction,
+ void *baton,
+ const Context &context,
+ lldb::addr_t addr, void *dst,
+ size_t length) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Read from Memory (address = 0x%" PRIx64 ", length = %" PRIu64
+ ", context = ",
+ addr, (uint64_t)length);
+ context.Dump(strm, instruction);
+ strm.EOL();
+ *((uint64_t *)dst) = 0xdeadbeef;
+ return length;
+}
+
+size_t EmulateInstruction::WriteMemoryDefault(EmulateInstruction *instruction,
+ void *baton,
+ const Context &context,
+ lldb::addr_t addr,
+ const void *dst, size_t length) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Write to Memory (address = 0x%" PRIx64 ", length = %" PRIu64
+ ", context = ",
+ addr, (uint64_t)length);
+ context.Dump(strm, instruction);
+ strm.EOL();
+ return length;
+}
+
+bool EmulateInstruction::ReadRegisterDefault(EmulateInstruction *instruction,
+ void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Read Register (%s)\n", reg_info->name);
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (GetBestRegisterKindAndNumber(reg_info, reg_kind, reg_num))
+ reg_value.SetUInt64((uint64_t)reg_kind << 24 | reg_num);
+ else
+ reg_value.SetUInt64(0);
+
+ return true;
+}
+
+bool EmulateInstruction::WriteRegisterDefault(EmulateInstruction *instruction,
+ void *baton,
+ const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ StreamFile strm(stdout, false);
+ strm.Printf(" Write to Register (name = %s, value = ", reg_info->name);
+ DumpRegisterValue(reg_value, &strm, reg_info, false, false, eFormatDefault);
+ strm.PutCString(", context = ");
+ context.Dump(strm, instruction);
+ strm.EOL();
+ return true;
+}
+
+void EmulateInstruction::Context::Dump(Stream &strm,
+ EmulateInstruction *instruction) const {
+ switch (type) {
+ case eContextReadOpcode:
+ strm.PutCString("reading opcode");
+ break;
+
+ case eContextImmediate:
+ strm.PutCString("immediate");
+ break;
+
+ case eContextPushRegisterOnStack:
+ strm.PutCString("push register");
+ break;
+
+ case eContextPopRegisterOffStack:
+ strm.PutCString("pop register");
+ break;
+
+ case eContextAdjustStackPointer:
+ strm.PutCString("adjust sp");
+ break;
+
+ case eContextSetFramePointer:
+ strm.PutCString("set frame pointer");
+ break;
+
+ case eContextAdjustBaseRegister:
+ strm.PutCString("adjusting (writing value back to) a base register");
+ break;
+
+ case eContextRegisterPlusOffset:
+ strm.PutCString("register + offset");
+ break;
+
+ case eContextRegisterStore:
+ strm.PutCString("store register");
+ break;
+
+ case eContextRegisterLoad:
+ strm.PutCString("load register");
+ break;
+
+ case eContextRelativeBranchImmediate:
+ strm.PutCString("relative branch immediate");
+ break;
+
+ case eContextAbsoluteBranchRegister:
+ strm.PutCString("absolute branch register");
+ break;
+
+ case eContextSupervisorCall:
+ strm.PutCString("supervisor call");
+ break;
+
+ case eContextTableBranchReadMemory:
+ strm.PutCString("table branch read memory");
+ break;
+
+ case eContextWriteRegisterRandomBits:
+ strm.PutCString("write random bits to a register");
+ break;
+
+ case eContextWriteMemoryRandomBits:
+ strm.PutCString("write random bits to a memory address");
+ break;
+
+ case eContextArithmetic:
+ strm.PutCString("arithmetic");
+ break;
+
+ case eContextReturnFromException:
+ strm.PutCString("return from exception");
+ break;
+
+ default:
+ strm.PutCString("unrecognized context.");
+ break;
+ }
+
+ switch (info_type) {
+ case eInfoTypeRegisterPlusOffset:
+ strm.Printf(" (reg_plus_offset = %s%+" PRId64 ")",
+ info.RegisterPlusOffset.reg.name,
+ info.RegisterPlusOffset.signed_offset);
+ break;
+
+ case eInfoTypeRegisterPlusIndirectOffset:
+ strm.Printf(" (reg_plus_reg = %s + %s)",
+ info.RegisterPlusIndirectOffset.base_reg.name,
+ info.RegisterPlusIndirectOffset.offset_reg.name);
+ break;
+
+ case eInfoTypeRegisterToRegisterPlusOffset:
+ strm.Printf(" (base_and_imm_offset = %s%+" PRId64 ", data_reg = %s)",
+ info.RegisterToRegisterPlusOffset.base_reg.name,
+ info.RegisterToRegisterPlusOffset.offset,
+ info.RegisterToRegisterPlusOffset.data_reg.name);
+ break;
+
+ case eInfoTypeRegisterToRegisterPlusIndirectOffset:
+ strm.Printf(" (base_and_reg_offset = %s + %s, data_reg = %s)",
+ info.RegisterToRegisterPlusIndirectOffset.base_reg.name,
+ info.RegisterToRegisterPlusIndirectOffset.offset_reg.name,
+ info.RegisterToRegisterPlusIndirectOffset.data_reg.name);
+ break;
+
+ case eInfoTypeRegisterRegisterOperands:
+ strm.Printf(" (register to register binary op: %s and %s)",
+ info.RegisterRegisterOperands.operand1.name,
+ info.RegisterRegisterOperands.operand2.name);
+ break;
+
+ case eInfoTypeOffset:
+ strm.Printf(" (signed_offset = %+" PRId64 ")", info.signed_offset);
+ break;
+
+ case eInfoTypeRegister:
+ strm.Printf(" (reg = %s)", info.reg.name);
+ break;
+
+ case eInfoTypeImmediate:
+ strm.Printf(" (unsigned_immediate = %" PRIu64 " (0x%16.16" PRIx64 "))",
+ info.unsigned_immediate, info.unsigned_immediate);
+ break;
+
+ case eInfoTypeImmediateSigned:
+ strm.Printf(" (signed_immediate = %+" PRId64 " (0x%16.16" PRIx64 "))",
+ info.signed_immediate, info.signed_immediate);
+ break;
+
+ case eInfoTypeAddress:
+ strm.Printf(" (address = 0x%" PRIx64 ")", info.address);
+ break;
+
+ case eInfoTypeISAAndImmediate:
+ strm.Printf(" (isa = %u, unsigned_immediate = %u (0x%8.8x))",
+ info.ISAAndImmediate.isa, info.ISAAndImmediate.unsigned_data32,
+ info.ISAAndImmediate.unsigned_data32);
+ break;
+
+ case eInfoTypeISAAndImmediateSigned:
+ strm.Printf(" (isa = %u, signed_immediate = %i (0x%8.8x))",
+ info.ISAAndImmediateSigned.isa,
+ info.ISAAndImmediateSigned.signed_data32,
+ info.ISAAndImmediateSigned.signed_data32);
+ break;
+
+ case eInfoTypeISA:
+ strm.Printf(" (isa = %u)", info.isa);
+ break;
+
+ case eInfoTypeNoArgs:
+ break;
+ }
+}
+
+bool EmulateInstruction::SetInstruction(const Opcode &opcode,
+ const Address &inst_addr,
+ Target *target) {
+ m_opcode = opcode;
+ m_addr = LLDB_INVALID_ADDRESS;
+ if (inst_addr.IsValid()) {
+ if (target != nullptr)
+ m_addr = inst_addr.GetLoadAddress(target);
+ if (m_addr == LLDB_INVALID_ADDRESS)
+ m_addr = inst_addr.GetFileAddress();
+ }
+ return true;
+}
+
+bool EmulateInstruction::GetBestRegisterKindAndNumber(
+ const RegisterInfo *reg_info, lldb::RegisterKind &reg_kind,
+ uint32_t &reg_num) {
+ // Generic and DWARF should be the two most popular register kinds when
+ // emulating instructions since they are the most platform agnostic...
+ reg_num = reg_info->kinds[eRegisterKindGeneric];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindGeneric;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindDWARF];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindDWARF;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindLLDB];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindLLDB;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindEHFrame];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindEHFrame;
+ return true;
+ }
+
+ reg_num = reg_info->kinds[eRegisterKindProcessPlugin];
+ if (reg_num != LLDB_INVALID_REGNUM) {
+ reg_kind = eRegisterKindProcessPlugin;
+ return true;
+ }
+ return false;
+}
+
+uint32_t
+EmulateInstruction::GetInternalRegisterNumber(RegisterContext *reg_ctx,
+ const RegisterInfo &reg_info) {
+ lldb::RegisterKind reg_kind;
+ uint32_t reg_num;
+ if (reg_ctx && GetBestRegisterKindAndNumber(&reg_info, reg_kind, reg_num))
+ return reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
+ return LLDB_INVALID_REGNUM;
+}
+
+bool EmulateInstruction::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) {
+ unwind_plan.Clear();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp b/contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp
new file mode 100644
index 000000000000..01df295398a8
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/FileLineResolver.cpp
@@ -0,0 +1,89 @@
+//===-- FileLineResolver.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/FileLineResolver.h"
+
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include <string>
+
+namespace lldb_private {
+class Address;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+// FileLineResolver:
+FileLineResolver::FileLineResolver(const FileSpec &file_spec, uint32_t line_no,
+ bool check_inlines)
+ : Searcher(), m_file_spec(file_spec), m_line_number(line_no),
+ m_inlines(check_inlines) {}
+
+FileLineResolver::~FileLineResolver() {}
+
+Searcher::CallbackReturn
+FileLineResolver::SearchCallback(SearchFilter &filter, SymbolContext &context,
+ Address *addr) {
+ CompileUnit *cu = context.comp_unit;
+
+ if (m_inlines ||
+ m_file_spec.Compare(*cu, m_file_spec, (bool)m_file_spec.GetDirectory())) {
+ uint32_t start_file_idx = 0;
+ uint32_t file_idx =
+ cu->GetSupportFiles().FindFileIndex(start_file_idx, m_file_spec, false);
+ if (file_idx != UINT32_MAX) {
+ LineTable *line_table = cu->GetLineTable();
+ if (line_table) {
+ if (m_line_number == 0) {
+ // Match all lines in a file...
+ const bool append = true;
+ while (file_idx != UINT32_MAX) {
+ line_table->FineLineEntriesForFileIndex(file_idx, append,
+ m_sc_list);
+ // Get the next file index in case we have multiple file entries
+ // for the same file
+ file_idx = cu->GetSupportFiles().FindFileIndex(file_idx + 1,
+ m_file_spec, false);
+ }
+ } else {
+ // Match a specific line in a file...
+ }
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth FileLineResolver::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+void FileLineResolver::GetDescription(Stream *s) {
+ s->Printf("File and line resolver for file: \"%s\" line: %u",
+ m_file_spec.GetPath().c_str(), m_line_number);
+}
+
+void FileLineResolver::Clear() {
+ m_file_spec.Clear();
+ m_line_number = UINT32_MAX;
+ m_sc_list.Clear();
+ m_inlines = true;
+}
+
+void FileLineResolver::Reset(const FileSpec &file_spec, uint32_t line,
+ bool check_inlines) {
+ m_file_spec = file_spec;
+ m_line_number = line;
+ m_sc_list.Clear();
+ m_inlines = check_inlines;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/FileSpecList.cpp b/contrib/llvm-project/lldb/source/Core/FileSpecList.cpp
new file mode 100644
index 000000000000..95133faf7502
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/FileSpecList.cpp
@@ -0,0 +1,121 @@
+//===-- FileSpecList.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/FileSpecList.h"
+
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Stream.h"
+
+#include <utility>
+
+#include <stdint.h>
+
+using namespace lldb_private;
+using namespace std;
+
+FileSpecList::FileSpecList() : m_files() {}
+
+FileSpecList::~FileSpecList() = default;
+
+// Append the "file_spec" to the end of the file spec list.
+void FileSpecList::Append(const FileSpec &file_spec) {
+ m_files.push_back(file_spec);
+}
+
+// Only append the "file_spec" if this list doesn't already contain it.
+//
+// Returns true if "file_spec" was added, false if this list already contained
+// a copy of "file_spec".
+bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) {
+ collection::iterator end = m_files.end();
+ if (find(m_files.begin(), end, file_spec) == end) {
+ m_files.push_back(file_spec);
+ return true;
+ }
+ return false;
+}
+
+// Clears the file list.
+void FileSpecList::Clear() { m_files.clear(); }
+
+// Dumps the file list to the supplied stream pointer "s".
+void FileSpecList::Dump(Stream *s, const char *separator_cstr) const {
+ collection::const_iterator pos, end = m_files.end();
+ for (pos = m_files.begin(); pos != end; ++pos) {
+ pos->Dump(s);
+ if (separator_cstr && ((pos + 1) != end))
+ s->PutCString(separator_cstr);
+ }
+}
+
+// Find the index of the file in the file spec list that matches "file_spec"
+// starting "start_idx" entries into the file spec list.
+//
+// Returns the valid index of the file that matches "file_spec" if it is found,
+// else std::numeric_limits<uint32_t>::max() is returned.
+size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,
+ bool full) const {
+ const size_t num_files = m_files.size();
+
+ // When looking for files, we will compare only the filename if the FILE_SPEC
+ // argument is empty
+ bool compare_filename_only = file_spec.GetDirectory().IsEmpty();
+
+ for (size_t idx = start_idx; idx < num_files; ++idx) {
+ if (compare_filename_only) {
+ if (ConstString::Equals(
+ m_files[idx].GetFilename(), file_spec.GetFilename(),
+ file_spec.IsCaseSensitive() || m_files[idx].IsCaseSensitive()))
+ return idx;
+ } else {
+ if (FileSpec::Equal(m_files[idx], file_spec, full))
+ return idx;
+ }
+ }
+
+ // We didn't find the file, return an invalid index
+ return UINT32_MAX;
+}
+
+// Returns the FileSpec object at index "idx". If "idx" is out of range, then
+// an empty FileSpec object will be returned.
+const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const {
+ if (idx < m_files.size())
+ return m_files[idx];
+ static FileSpec g_empty_file_spec;
+ return g_empty_file_spec;
+}
+
+const FileSpec *FileSpecList::GetFileSpecPointerAtIndex(size_t idx) const {
+ if (idx < m_files.size())
+ return &m_files[idx];
+ return nullptr;
+}
+
+// Return the size in bytes that this object takes in memory. This returns the
+// size in bytes of this object's member variables and any FileSpec objects its
+// member variables contain, the result doesn't not include the string values
+// for the directories any filenames as those are in shared string pools.
+size_t FileSpecList::MemorySize() const {
+ size_t mem_size = sizeof(FileSpecList);
+ collection::const_iterator pos, end = m_files.end();
+ for (pos = m_files.begin(); pos != end; ++pos) {
+ mem_size += pos->MemorySize();
+ }
+
+ return mem_size;
+}
+
+// Return the number of files in the file spec list.
+size_t FileSpecList::GetSize() const { return m_files.size(); }
+
+size_t FileSpecList::GetFilesMatchingPartialPath(const char *path,
+ bool dir_okay,
+ FileSpecList &matches) {
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/FormatEntity.cpp b/contrib/llvm-project/lldb/source/Core/FormatEntity.cpp
new file mode 100644
index 000000000000..c90828f40989
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/FormatEntity.cpp
@@ -0,0 +1,2414 @@
+//===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/FormatEntity.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DumpRegisterValue.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/FormatClasses.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/SharingPtr.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Compiler.h"
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <type_traits>
+#include <utility>
+
+namespace lldb_private {
+class ScriptInterpreter;
+}
+namespace lldb_private {
+struct RegisterInfo;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum FileKind { FileError = 0, Basename, Dirname, Fullpath };
+
+#define ENTRY(n, t) \
+ { n, nullptr, FormatEntity::Entry::Type::t, 0, 0, nullptr, false }
+#define ENTRY_VALUE(n, t, v) \
+ { n, nullptr, FormatEntity::Entry::Type::t, v, 0, nullptr, false }
+#define ENTRY_CHILDREN(n, t, c) \
+ { \
+ n, nullptr, FormatEntity::Entry::Type::t, 0, \
+ static_cast<uint32_t>(llvm::array_lengthof(c)), c, false \
+ }
+#define ENTRY_CHILDREN_KEEP_SEP(n, t, c) \
+ { \
+ n, nullptr, FormatEntity::Entry::Type::t, 0, \
+ static_cast<uint32_t>(llvm::array_lengthof(c)), c, true \
+ }
+#define ENTRY_STRING(n, s) \
+ { n, s, FormatEntity::Entry::Type::EscapeCode, 0, 0, nullptr, false }
+static FormatEntity::Entry::Definition g_string_entry[] = {
+ ENTRY("*", ParentString)};
+
+static FormatEntity::Entry::Definition g_addr_entries[] = {
+ ENTRY("load", AddressLoad),
+ ENTRY("file", AddressFile),
+ ENTRY("load", AddressLoadOrFile),
+};
+
+static FormatEntity::Entry::Definition g_file_child_entries[] = {
+ ENTRY_VALUE("basename", ParentNumber, FileKind::Basename),
+ ENTRY_VALUE("dirname", ParentNumber, FileKind::Dirname),
+ ENTRY_VALUE("fullpath", ParentNumber, FileKind::Fullpath)};
+
+static FormatEntity::Entry::Definition g_frame_child_entries[] = {
+ ENTRY("index", FrameIndex),
+ ENTRY("pc", FrameRegisterPC),
+ ENTRY("fp", FrameRegisterFP),
+ ENTRY("sp", FrameRegisterSP),
+ ENTRY("flags", FrameRegisterFlags),
+ ENTRY("no-debug", FrameNoDebug),
+ ENTRY_CHILDREN("reg", FrameRegisterByName, g_string_entry),
+ ENTRY("is-artificial", FrameIsArtificial),
+};
+
+static FormatEntity::Entry::Definition g_function_child_entries[] = {
+ ENTRY("id", FunctionID),
+ ENTRY("name", FunctionName),
+ ENTRY("name-without-args", FunctionNameNoArgs),
+ ENTRY("name-with-args", FunctionNameWithArgs),
+ ENTRY("addr-offset", FunctionAddrOffset),
+ ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete),
+ ENTRY("line-offset", FunctionLineOffset),
+ ENTRY("pc-offset", FunctionPCOffset),
+ ENTRY("initial-function", FunctionInitial),
+ ENTRY("changed", FunctionChanged),
+ ENTRY("is-optimized", FunctionIsOptimized)};
+
+static FormatEntity::Entry::Definition g_line_child_entries[] = {
+ ENTRY_CHILDREN("file", LineEntryFile, g_file_child_entries),
+ ENTRY("number", LineEntryLineNumber),
+ ENTRY("column", LineEntryColumn),
+ ENTRY("start-addr", LineEntryStartAddress),
+ ENTRY("end-addr", LineEntryEndAddress),
+};
+
+static FormatEntity::Entry::Definition g_module_child_entries[] = {
+ ENTRY_CHILDREN("file", ModuleFile, g_file_child_entries),
+};
+
+static FormatEntity::Entry::Definition g_process_child_entries[] = {
+ ENTRY("id", ProcessID),
+ ENTRY_VALUE("name", ProcessFile, FileKind::Basename),
+ ENTRY_CHILDREN("file", ProcessFile, g_file_child_entries),
+};
+
+static FormatEntity::Entry::Definition g_svar_child_entries[] = {
+ ENTRY("*", ParentString)};
+
+static FormatEntity::Entry::Definition g_var_child_entries[] = {
+ ENTRY("*", ParentString)};
+
+static FormatEntity::Entry::Definition g_thread_child_entries[] = {
+ ENTRY("id", ThreadID),
+ ENTRY("protocol_id", ThreadProtocolID),
+ ENTRY("index", ThreadIndexID),
+ ENTRY_CHILDREN("info", ThreadInfo, g_string_entry),
+ ENTRY("queue", ThreadQueue),
+ ENTRY("name", ThreadName),
+ ENTRY("stop-reason", ThreadStopReason),
+ ENTRY("return-value", ThreadReturnValue),
+ ENTRY("completed-expression", ThreadCompletedExpression),
+};
+
+static FormatEntity::Entry::Definition g_target_child_entries[] = {
+ ENTRY("arch", TargetArch),
+};
+
+#define _TO_STR2(_val) #_val
+#define _TO_STR(_val) _TO_STR2(_val)
+
+static FormatEntity::Entry::Definition g_ansi_fg_entries[] = {
+ ENTRY_STRING("black",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END),
+ ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END),
+ ENTRY_STRING("green",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END),
+ ENTRY_STRING("yellow",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END),
+ ENTRY_STRING("blue",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END),
+ ENTRY_STRING("purple",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END),
+ ENTRY_STRING("cyan",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END),
+ ENTRY_STRING("white",
+ ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END),
+};
+
+static FormatEntity::Entry::Definition g_ansi_bg_entries[] = {
+ ENTRY_STRING("black",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END),
+ ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END),
+ ENTRY_STRING("green",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END),
+ ENTRY_STRING("yellow",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END),
+ ENTRY_STRING("blue",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END),
+ ENTRY_STRING("purple",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END),
+ ENTRY_STRING("cyan",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END),
+ ENTRY_STRING("white",
+ ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END),
+};
+
+static FormatEntity::Entry::Definition g_ansi_entries[] = {
+ ENTRY_CHILDREN("fg", Invalid, g_ansi_fg_entries),
+ ENTRY_CHILDREN("bg", Invalid, g_ansi_bg_entries),
+ ENTRY_STRING("normal",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END),
+ ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END),
+ ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END),
+ ENTRY_STRING("italic",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END),
+ ENTRY_STRING("underline",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END),
+ ENTRY_STRING("slow-blink",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END),
+ ENTRY_STRING("fast-blink",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END),
+ ENTRY_STRING("negative",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END),
+ ENTRY_STRING("conceal",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END),
+ ENTRY_STRING("crossed-out",
+ ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END),
+};
+
+static FormatEntity::Entry::Definition g_script_child_entries[] = {
+ ENTRY("frame", ScriptFrame), ENTRY("process", ScriptProcess),
+ ENTRY("target", ScriptTarget), ENTRY("thread", ScriptThread),
+ ENTRY("var", ScriptVariable), ENTRY("svar", ScriptVariableSynthetic),
+ ENTRY("thread", ScriptThread),
+};
+
+static FormatEntity::Entry::Definition g_top_level_entries[] = {
+ ENTRY_CHILDREN("addr", AddressLoadOrFile, g_addr_entries),
+ ENTRY("addr-file-or-load", AddressLoadOrFile),
+ ENTRY_CHILDREN("ansi", Invalid, g_ansi_entries),
+ ENTRY("current-pc-arrow", CurrentPCArrow),
+ ENTRY_CHILDREN("file", File, g_file_child_entries),
+ ENTRY("language", Lang),
+ ENTRY_CHILDREN("frame", Invalid, g_frame_child_entries),
+ ENTRY_CHILDREN("function", Invalid, g_function_child_entries),
+ ENTRY_CHILDREN("line", Invalid, g_line_child_entries),
+ ENTRY_CHILDREN("module", Invalid, g_module_child_entries),
+ ENTRY_CHILDREN("process", Invalid, g_process_child_entries),
+ ENTRY_CHILDREN("script", Invalid, g_script_child_entries),
+ ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, g_svar_child_entries),
+ ENTRY_CHILDREN("thread", Invalid, g_thread_child_entries),
+ ENTRY_CHILDREN("target", Invalid, g_target_child_entries),
+ ENTRY_CHILDREN_KEEP_SEP("var", Variable, g_var_child_entries),
+};
+
+static FormatEntity::Entry::Definition g_root =
+ ENTRY_CHILDREN("<root>", Root, g_top_level_entries);
+
+FormatEntity::Entry::Entry(llvm::StringRef s)
+ : string(s.data(), s.size()), printf_format(), children(),
+ definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault),
+ number(0), deref(false) {}
+
+FormatEntity::Entry::Entry(char ch)
+ : string(1, ch), printf_format(), children(), definition(nullptr),
+ type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {}
+
+void FormatEntity::Entry::AppendChar(char ch) {
+ if (children.empty() || children.back().type != Entry::Type::String)
+ children.push_back(Entry(ch));
+ else
+ children.back().string.append(1, ch);
+}
+
+void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
+ if (children.empty() || children.back().type != Entry::Type::String)
+ children.push_back(Entry(s));
+ else
+ children.back().string.append(s.data(), s.size());
+}
+
+void FormatEntity::Entry::AppendText(const char *cstr) {
+ return AppendText(llvm::StringRef(cstr));
+}
+
+Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) {
+ entry.Clear();
+ entry.type = Entry::Type::Root;
+ llvm::StringRef modifiable_format(format_str);
+ return ParseInternal(modifiable_format, entry, 0);
+}
+
+#define ENUM_TO_CSTR(eee) \
+ case FormatEntity::Entry::Type::eee: \
+ return #eee
+
+const char *FormatEntity::Entry::TypeToCString(Type t) {
+ switch (t) {
+ ENUM_TO_CSTR(Invalid);
+ ENUM_TO_CSTR(ParentNumber);
+ ENUM_TO_CSTR(ParentString);
+ ENUM_TO_CSTR(EscapeCode);
+ ENUM_TO_CSTR(Root);
+ ENUM_TO_CSTR(String);
+ ENUM_TO_CSTR(Scope);
+ ENUM_TO_CSTR(Variable);
+ ENUM_TO_CSTR(VariableSynthetic);
+ ENUM_TO_CSTR(ScriptVariable);
+ ENUM_TO_CSTR(ScriptVariableSynthetic);
+ ENUM_TO_CSTR(AddressLoad);
+ ENUM_TO_CSTR(AddressFile);
+ ENUM_TO_CSTR(AddressLoadOrFile);
+ ENUM_TO_CSTR(ProcessID);
+ ENUM_TO_CSTR(ProcessFile);
+ ENUM_TO_CSTR(ScriptProcess);
+ ENUM_TO_CSTR(ThreadID);
+ ENUM_TO_CSTR(ThreadProtocolID);
+ ENUM_TO_CSTR(ThreadIndexID);
+ ENUM_TO_CSTR(ThreadName);
+ ENUM_TO_CSTR(ThreadQueue);
+ ENUM_TO_CSTR(ThreadStopReason);
+ ENUM_TO_CSTR(ThreadReturnValue);
+ ENUM_TO_CSTR(ThreadCompletedExpression);
+ ENUM_TO_CSTR(ScriptThread);
+ ENUM_TO_CSTR(ThreadInfo);
+ ENUM_TO_CSTR(TargetArch);
+ ENUM_TO_CSTR(ScriptTarget);
+ ENUM_TO_CSTR(ModuleFile);
+ ENUM_TO_CSTR(File);
+ ENUM_TO_CSTR(Lang);
+ ENUM_TO_CSTR(FrameIndex);
+ ENUM_TO_CSTR(FrameNoDebug);
+ ENUM_TO_CSTR(FrameRegisterPC);
+ ENUM_TO_CSTR(FrameRegisterSP);
+ ENUM_TO_CSTR(FrameRegisterFP);
+ ENUM_TO_CSTR(FrameRegisterFlags);
+ ENUM_TO_CSTR(FrameRegisterByName);
+ ENUM_TO_CSTR(FrameIsArtificial);
+ ENUM_TO_CSTR(ScriptFrame);
+ ENUM_TO_CSTR(FunctionID);
+ ENUM_TO_CSTR(FunctionDidChange);
+ ENUM_TO_CSTR(FunctionInitialFunction);
+ ENUM_TO_CSTR(FunctionName);
+ ENUM_TO_CSTR(FunctionNameWithArgs);
+ ENUM_TO_CSTR(FunctionNameNoArgs);
+ ENUM_TO_CSTR(FunctionAddrOffset);
+ ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
+ ENUM_TO_CSTR(FunctionLineOffset);
+ ENUM_TO_CSTR(FunctionPCOffset);
+ ENUM_TO_CSTR(FunctionInitial);
+ ENUM_TO_CSTR(FunctionChanged);
+ ENUM_TO_CSTR(FunctionIsOptimized);
+ ENUM_TO_CSTR(LineEntryFile);
+ ENUM_TO_CSTR(LineEntryLineNumber);
+ ENUM_TO_CSTR(LineEntryColumn);
+ ENUM_TO_CSTR(LineEntryStartAddress);
+ ENUM_TO_CSTR(LineEntryEndAddress);
+ ENUM_TO_CSTR(CurrentPCArrow);
+ }
+ return "???";
+}
+
+#undef ENUM_TO_CSTR
+
+void FormatEntity::Entry::Dump(Stream &s, int depth) const {
+ s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type));
+ if (fmt != eFormatDefault)
+ s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt));
+ if (!string.empty())
+ s.Printf("string = \"%s\"", string.c_str());
+ if (!printf_format.empty())
+ s.Printf("printf_format = \"%s\"", printf_format.c_str());
+ if (number != 0)
+ s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number);
+ if (deref)
+ s.Printf("deref = true, ");
+ s.EOL();
+ for (const auto &child : children) {
+ child.Dump(s, depth + 1);
+ }
+}
+
+template <typename T>
+static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx, T t,
+ const char *script_function_name) {
+ Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
+
+ if (target) {
+ ScriptInterpreter *script_interpreter =
+ target->GetDebugger().GetScriptInterpreter();
+ if (script_interpreter) {
+ Status error;
+ std::string script_output;
+
+ if (script_interpreter->RunScriptFormatKeyword(script_function_name, t,
+ script_output, error) &&
+ error.Success()) {
+ s.Printf("%s", script_output.c_str());
+ return true;
+ } else {
+ s.Printf("<error: %s>", error.AsCString());
+ }
+ }
+ }
+ return false;
+}
+
+static bool DumpAddress(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx, const Address &addr,
+ bool print_file_addr_or_load_addr) {
+ Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
+ addr_t vaddr = LLDB_INVALID_ADDRESS;
+ if (exe_ctx && !target->GetSectionLoadList().IsEmpty())
+ vaddr = addr.GetLoadAddress(target);
+ if (vaddr == LLDB_INVALID_ADDRESS)
+ vaddr = addr.GetFileAddress();
+
+ if (vaddr != LLDB_INVALID_ADDRESS) {
+ int addr_width = 0;
+ if (exe_ctx && target) {
+ addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
+ }
+ if (addr_width == 0)
+ addr_width = 16;
+ if (print_file_addr_or_load_addr) {
+ ExecutionContextScope *exe_scope = nullptr;
+ if (exe_ctx)
+ exe_scope = exe_ctx->GetBestExecutionContextScope();
+ addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress, 0);
+ } else {
+ s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
+ }
+ return true;
+ }
+ return false;
+}
+
+static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const Address &format_addr,
+ bool concrete_only, bool no_padding,
+ bool print_zero_offsets) {
+ if (format_addr.IsValid()) {
+ Address func_addr;
+
+ if (sc) {
+ if (sc->function) {
+ func_addr = sc->function->GetAddressRange().GetBaseAddress();
+ if (sc->block && !concrete_only) {
+ // Check to make sure we aren't in an inline function. If we are, use
+ // the inline block range that contains "format_addr" since blocks
+ // can be discontiguous.
+ Block *inline_block = sc->block->GetContainingInlinedBlock();
+ AddressRange inline_range;
+ if (inline_block && inline_block->GetRangeContainingAddress(
+ format_addr, inline_range))
+ func_addr = inline_range.GetBaseAddress();
+ }
+ } else if (sc->symbol && sc->symbol->ValueIsAddress())
+ func_addr = sc->symbol->GetAddressRef();
+ }
+
+ if (func_addr.IsValid()) {
+ const char *addr_offset_padding = no_padding ? "" : " ";
+
+ if (func_addr.GetSection() == format_addr.GetSection()) {
+ addr_t func_file_addr = func_addr.GetFileAddress();
+ addr_t addr_file_addr = format_addr.GetFileAddress();
+ if (addr_file_addr > func_file_addr ||
+ (addr_file_addr == func_file_addr && print_zero_offsets)) {
+ s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ addr_file_addr - func_file_addr);
+ } else if (addr_file_addr < func_file_addr) {
+ s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ func_file_addr - addr_file_addr);
+ }
+ return true;
+ } else {
+ Target *target = Target::GetTargetFromContexts(exe_ctx, sc);
+ if (target) {
+ addr_t func_load_addr = func_addr.GetLoadAddress(target);
+ addr_t addr_load_addr = format_addr.GetLoadAddress(target);
+ if (addr_load_addr > func_load_addr ||
+ (addr_load_addr == func_load_addr && print_zero_offsets)) {
+ s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ addr_load_addr - func_load_addr);
+ } else if (addr_load_addr < func_load_addr) {
+ s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding,
+ func_load_addr - addr_load_addr);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool ScanBracketedRange(llvm::StringRef subpath,
+ size_t &close_bracket_index,
+ const char *&var_name_final_if_array_range,
+ int64_t &index_lower, int64_t &index_higher) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ close_bracket_index = llvm::StringRef::npos;
+ const size_t open_bracket_index = subpath.find('[');
+ if (open_bracket_index == llvm::StringRef::npos) {
+ LLDB_LOGF(log,
+ "[ScanBracketedRange] no bracketed range, skipping entirely");
+ return false;
+ }
+
+ close_bracket_index = subpath.find(']', open_bracket_index + 1);
+
+ if (close_bracket_index == llvm::StringRef::npos) {
+ LLDB_LOGF(log,
+ "[ScanBracketedRange] no bracketed range, skipping entirely");
+ return false;
+ } else {
+ var_name_final_if_array_range = subpath.data() + open_bracket_index;
+
+ if (close_bracket_index - open_bracket_index == 1) {
+ LLDB_LOGF(
+ log,
+ "[ScanBracketedRange] '[]' detected.. going from 0 to end of data");
+ index_lower = 0;
+ } else {
+ const size_t separator_index = subpath.find('-', open_bracket_index + 1);
+
+ if (separator_index == llvm::StringRef::npos) {
+ const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
+ index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
+ index_higher = index_lower;
+ LLDB_LOGF(log,
+ "[ScanBracketedRange] [%" PRId64
+ "] detected, high index is same",
+ index_lower);
+ } else {
+ const char *index_lower_cstr = subpath.data() + open_bracket_index + 1;
+ const char *index_higher_cstr = subpath.data() + separator_index + 1;
+ index_lower = ::strtoul(index_lower_cstr, nullptr, 0);
+ index_higher = ::strtoul(index_higher_cstr, nullptr, 0);
+ LLDB_LOGF(log,
+ "[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected",
+ index_lower, index_higher);
+ }
+ if (index_lower > index_higher && index_higher > 0) {
+ LLDB_LOGF(log, "[ScanBracketedRange] swapping indices");
+ const int64_t temp = index_lower;
+ index_lower = index_higher;
+ index_higher = temp;
+ }
+ }
+ }
+ return true;
+}
+
+static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) {
+ switch (file_kind) {
+ case FileKind::FileError:
+ break;
+
+ case FileKind::Basename:
+ if (file.GetFilename()) {
+ s << file.GetFilename();
+ return true;
+ }
+ break;
+
+ case FileKind::Dirname:
+ if (file.GetDirectory()) {
+ s << file.GetDirectory();
+ return true;
+ }
+ break;
+
+ case FileKind::Fullpath:
+ if (file) {
+ s << file;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind,
+ uint32_t reg_num, Format format)
+
+{
+ if (frame) {
+ RegisterContext *reg_ctx = frame->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ const uint32_t lldb_reg_num =
+ reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
+ if (lldb_reg_num != LLDB_INVALID_REGNUM) {
+ const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num);
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index,
+ StackFrame *frame,
+ bool deref_pointer) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ const char *ptr_deref_format = "[%d]";
+ std::string ptr_deref_buffer(10, 0);
+ ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index);
+ LLDB_LOGF(log, "[ExpandIndexedExpression] name to deref: %s",
+ ptr_deref_buffer.c_str());
+ ValueObject::GetValueForExpressionPathOptions options;
+ ValueObject::ExpressionPathEndResultType final_value_type;
+ ValueObject::ExpressionPathScanEndReason reason_to_stop;
+ ValueObject::ExpressionPathAftermath what_next =
+ (deref_pointer ? ValueObject::eExpressionPathAftermathDereference
+ : ValueObject::eExpressionPathAftermathNothing);
+ ValueObjectSP item = valobj->GetValueForExpressionPath(
+ ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options,
+ &what_next);
+ if (!item) {
+ LLDB_LOGF(log,
+ "[ExpandIndexedExpression] ERROR: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ } else {
+ LLDB_LOGF(log,
+ "[ExpandIndexedExpression] ALL RIGHT: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ }
+ return item;
+}
+
+static char ConvertValueObjectStyleToChar(
+ ValueObject::ValueObjectRepresentationStyle style) {
+ switch (style) {
+ case ValueObject::eValueObjectRepresentationStyleLanguageSpecific:
+ return '@';
+ case ValueObject::eValueObjectRepresentationStyleValue:
+ return 'V';
+ case ValueObject::eValueObjectRepresentationStyleLocation:
+ return 'L';
+ case ValueObject::eValueObjectRepresentationStyleSummary:
+ return 'S';
+ case ValueObject::eValueObjectRepresentationStyleChildrenCount:
+ return '#';
+ case ValueObject::eValueObjectRepresentationStyleType:
+ return 'T';
+ case ValueObject::eValueObjectRepresentationStyleName:
+ return 'N';
+ case ValueObject::eValueObjectRepresentationStyleExpressionPath:
+ return '>';
+ }
+ return '\0';
+}
+
+static bool DumpValue(Stream &s, const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const FormatEntity::Entry &entry, ValueObject *valobj) {
+ if (valobj == nullptr)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ Format custom_format = eFormatInvalid;
+ ValueObject::ValueObjectRepresentationStyle val_obj_display =
+ entry.string.empty()
+ ? ValueObject::eValueObjectRepresentationStyleValue
+ : ValueObject::eValueObjectRepresentationStyleSummary;
+
+ bool do_deref_pointer = entry.deref;
+ bool is_script = false;
+ switch (entry.type) {
+ case FormatEntity::Entry::Type::ScriptVariable:
+ is_script = true;
+ break;
+
+ case FormatEntity::Entry::Type::Variable:
+ custom_format = entry.fmt;
+ val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
+ break;
+
+ case FormatEntity::Entry::Type::ScriptVariableSynthetic:
+ is_script = true;
+ LLVM_FALLTHROUGH;
+ case FormatEntity::Entry::Type::VariableSynthetic:
+ custom_format = entry.fmt;
+ val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number;
+ if (!valobj->IsSynthetic()) {
+ valobj = valobj->GetSyntheticValue().get();
+ if (valobj == nullptr)
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ if (valobj == nullptr)
+ return false;
+
+ ValueObject::ExpressionPathAftermath what_next =
+ (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference
+ : ValueObject::eExpressionPathAftermathNothing);
+ ValueObject::GetValueForExpressionPathOptions options;
+ options.DontCheckDotVsArrowSyntax()
+ .DoAllowBitfieldSyntax()
+ .DoAllowFragileIVar()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::Both);
+ ValueObject *target = nullptr;
+ const char *var_name_final_if_array_range = nullptr;
+ size_t close_bracket_index = llvm::StringRef::npos;
+ int64_t index_lower = -1;
+ int64_t index_higher = -1;
+ bool is_array_range = false;
+ bool was_plain_var = false;
+ bool was_var_format = false;
+ bool was_var_indexed = false;
+ ValueObject::ExpressionPathScanEndReason reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEndOfString;
+ ValueObject::ExpressionPathEndResultType final_value_type =
+ ValueObject::eExpressionPathEndResultTypePlain;
+
+ if (is_script) {
+ return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str());
+ }
+
+ llvm::StringRef subpath(entry.string);
+ // simplest case ${var}, just print valobj's value
+ if (entry.string.empty()) {
+ if (entry.printf_format.empty() && entry.fmt == eFormatDefault &&
+ entry.number == ValueObject::eValueObjectRepresentationStyleValue)
+ was_plain_var = true;
+ else
+ was_var_format = true;
+ target = valobj;
+ } else // this is ${var.something} or multiple .something nested
+ {
+ if (entry.string[0] == '[')
+ was_var_indexed = true;
+ ScanBracketedRange(subpath, close_bracket_index,
+ var_name_final_if_array_range, index_lower,
+ index_higher);
+
+ Status error;
+
+ const std::string &expr_path = entry.string;
+
+ LLDB_LOGF(log, "[Debugger::FormatPrompt] symbol to expand: %s",
+ expr_path.c_str());
+
+ target =
+ valobj
+ ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop,
+ &final_value_type, options, &what_next)
+ .get();
+
+ if (!target) {
+ LLDB_LOGF(log,
+ "[Debugger::FormatPrompt] ERROR: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ return false;
+ } else {
+ LLDB_LOGF(log,
+ "[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d,"
+ " final_value_type %d",
+ reason_to_stop, final_value_type);
+ target = target
+ ->GetQualifiedRepresentationIfAvailable(
+ target->GetDynamicValueType(), true)
+ .get();
+ }
+ }
+
+ is_array_range =
+ (final_value_type ==
+ ValueObject::eExpressionPathEndResultTypeBoundedRange ||
+ final_value_type ==
+ ValueObject::eExpressionPathEndResultTypeUnboundedRange);
+
+ do_deref_pointer =
+ (what_next == ValueObject::eExpressionPathAftermathDereference);
+
+ if (do_deref_pointer && !is_array_range) {
+ // I have not deref-ed yet, let's do it
+ // this happens when we are not going through
+ // GetValueForVariableExpressionPath to get to the target ValueObject
+ Status error;
+ target = target->Dereference(error).get();
+ if (error.Fail()) {
+ LLDB_LOGF(log, "[Debugger::FormatPrompt] ERROR: %s\n",
+ error.AsCString("unknown"));
+ return false;
+ }
+ do_deref_pointer = false;
+ }
+
+ if (!target) {
+ LLDB_LOGF(log, "[Debugger::FormatPrompt] could not calculate target for "
+ "prompt expression");
+ return false;
+ }
+
+ // we do not want to use the summary for a bitfield of type T:n if we were
+ // originally dealing with just a T - that would get us into an endless
+ // recursion
+ if (target->IsBitfield() && was_var_indexed) {
+ // TODO: check for a (T:n)-specific summary - we should still obey that
+ StreamString bitfield_name;
+ bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(),
+ target->GetBitfieldBitSize());
+ auto type_sp = std::make_shared<TypeNameSpecifierImpl>(
+ bitfield_name.GetString(), false);
+ if (val_obj_display ==
+ ValueObject::eValueObjectRepresentationStyleSummary &&
+ !DataVisualization::GetSummaryForType(type_sp))
+ val_obj_display = ValueObject::eValueObjectRepresentationStyleValue;
+ }
+
+ // TODO use flags for these
+ const uint32_t type_info_flags =
+ target->GetCompilerType().GetTypeInfo(nullptr);
+ bool is_array = (type_info_flags & eTypeIsArray) != 0;
+ bool is_pointer = (type_info_flags & eTypeIsPointer) != 0;
+ bool is_aggregate = target->GetCompilerType().IsAggregateType();
+
+ if ((is_array || is_pointer) && (!is_array_range) &&
+ val_obj_display ==
+ ValueObject::eValueObjectRepresentationStyleValue) // this should be
+ // wrong, but there
+ // are some
+ // exceptions
+ {
+ StreamString str_temp;
+ LLDB_LOGF(log,
+ "[Debugger::FormatPrompt] I am into array || pointer && !range");
+
+ if (target->HasSpecialPrintableRepresentation(val_obj_display,
+ custom_format)) {
+ // try to use the special cases
+ bool success = target->DumpPrintableRepresentation(
+ str_temp, val_obj_display, custom_format);
+ LLDB_LOGF(log, "[Debugger::FormatPrompt] special cases did%s match",
+ success ? "" : "n't");
+
+ // should not happen
+ if (success)
+ s << str_temp.GetString();
+ return true;
+ } else {
+ if (was_plain_var) // if ${var}
+ {
+ s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
+ } else if (is_pointer) // if pointer, value is the address stored
+ {
+ target->DumpPrintableRepresentation(
+ s, val_obj_display, custom_format,
+ ValueObject::PrintableRepresentationSpecialCases::eDisable);
+ }
+ return true;
+ }
+ }
+
+ // if directly trying to print ${var}, and this is an aggregate, display a
+ // nice type @ location message
+ if (is_aggregate && was_plain_var) {
+ s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
+ return true;
+ }
+
+ // if directly trying to print ${var%V}, and this is an aggregate, do not let
+ // the user do it
+ if (is_aggregate &&
+ ((was_var_format &&
+ val_obj_display ==
+ ValueObject::eValueObjectRepresentationStyleValue))) {
+ s << "<invalid use of aggregate type>";
+ return true;
+ }
+
+ if (!is_array_range) {
+ LLDB_LOGF(log,
+ "[Debugger::FormatPrompt] dumping ordinary printable output");
+ return target->DumpPrintableRepresentation(s, val_obj_display,
+ custom_format);
+ } else {
+ LLDB_LOGF(log,
+ "[Debugger::FormatPrompt] checking if I can handle as array");
+ if (!is_array && !is_pointer)
+ return false;
+ LLDB_LOGF(log, "[Debugger::FormatPrompt] handle as array");
+ StreamString special_directions_stream;
+ llvm::StringRef special_directions;
+ if (close_bracket_index != llvm::StringRef::npos &&
+ subpath.size() > close_bracket_index) {
+ ConstString additional_data(subpath.drop_front(close_bracket_index + 1));
+ special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "",
+ additional_data.GetCString());
+
+ if (entry.fmt != eFormatDefault) {
+ const char format_char =
+ FormatManager::GetFormatAsFormatChar(entry.fmt);
+ if (format_char != '\0')
+ special_directions_stream.Printf("%%%c", format_char);
+ else {
+ const char *format_cstr =
+ FormatManager::GetFormatAsCString(entry.fmt);
+ special_directions_stream.Printf("%%%s", format_cstr);
+ }
+ } else if (entry.number != 0) {
+ const char style_char = ConvertValueObjectStyleToChar(
+ (ValueObject::ValueObjectRepresentationStyle)entry.number);
+ if (style_char)
+ special_directions_stream.Printf("%%%c", style_char);
+ }
+ special_directions_stream.PutChar('}');
+ special_directions =
+ llvm::StringRef(special_directions_stream.GetString());
+ }
+
+ // let us display items index_lower thru index_higher of this array
+ s.PutChar('[');
+
+ if (index_higher < 0)
+ index_higher = valobj->GetNumChildren() - 1;
+
+ uint32_t max_num_children =
+ target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
+
+ bool success = true;
+ for (int64_t index = index_lower; index <= index_higher; ++index) {
+ ValueObject *item =
+ ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false)
+ .get();
+
+ if (!item) {
+ LLDB_LOGF(log,
+ "[Debugger::FormatPrompt] ERROR in getting child item at "
+ "index %" PRId64,
+ index);
+ } else {
+ LLDB_LOGF(
+ log,
+ "[Debugger::FormatPrompt] special_directions for child item: %s",
+ special_directions.data() ? special_directions.data() : "");
+ }
+
+ if (special_directions.empty()) {
+ success &= item->DumpPrintableRepresentation(s, val_obj_display,
+ custom_format);
+ } else {
+ success &= FormatEntity::FormatStringRef(
+ special_directions, s, sc, exe_ctx, nullptr, item, false, false);
+ }
+
+ if (--max_num_children == 0) {
+ s.PutCString(", ...");
+ break;
+ }
+
+ if (index < index_higher)
+ s.PutChar(',');
+ }
+ s.PutChar(']');
+ return success;
+ }
+}
+
+static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name,
+ Format format) {
+ if (frame) {
+ RegisterContext *reg_ctx = frame->GetRegisterContext().get();
+
+ if (reg_ctx) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ DumpRegisterValue(reg_value, &s, reg_info, false, false, format);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static bool FormatThreadExtendedInfoRecurse(
+ const FormatEntity::Entry &entry,
+ const StructuredData::ObjectSP &thread_info_dictionary,
+ const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) {
+ llvm::StringRef path(entry.string);
+
+ StructuredData::ObjectSP value =
+ thread_info_dictionary->GetObjectForDotSeparatedPath(path);
+
+ if (value) {
+ if (value->GetType() == eStructuredDataTypeInteger) {
+ const char *token_format = "0x%4.4" PRIx64;
+ if (!entry.printf_format.empty())
+ token_format = entry.printf_format.c_str();
+ s.Printf(token_format, value->GetAsInteger()->GetValue());
+ return true;
+ } else if (value->GetType() == eStructuredDataTypeFloat) {
+ s.Printf("%f", value->GetAsFloat()->GetValue());
+ return true;
+ } else if (value->GetType() == eStructuredDataTypeString) {
+ s.Format("{0}", value->GetAsString()->GetValue());
+ return true;
+ } else if (value->GetType() == eStructuredDataTypeArray) {
+ if (value->GetAsArray()->GetSize() > 0) {
+ s.Printf("%zu", value->GetAsArray()->GetSize());
+ return true;
+ }
+ } else if (value->GetType() == eStructuredDataTypeDictionary) {
+ s.Printf("%zu",
+ value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static inline bool IsToken(const char *var_name_begin, const char *var) {
+ return (::strncmp(var_name_begin, var, strlen(var)) == 0);
+}
+
+bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
+ const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr, ValueObject *valobj,
+ bool function_changed,
+ bool initial_function) {
+ if (!format_str.empty()) {
+ FormatEntity::Entry root;
+ Status error = FormatEntity::Parse(format_str, root);
+ if (error.Success()) {
+ return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
+ function_changed, initial_function);
+ }
+ }
+ return false;
+}
+
+bool FormatEntity::FormatCString(const char *format, Stream &s,
+ const SymbolContext *sc,
+ const ExecutionContext *exe_ctx,
+ const Address *addr, ValueObject *valobj,
+ bool function_changed, bool initial_function) {
+ if (format && format[0]) {
+ FormatEntity::Entry root;
+ llvm::StringRef format_str(format);
+ Status error = FormatEntity::Parse(format_str, root);
+ if (error.Success()) {
+ return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj,
+ function_changed, initial_function);
+ }
+ }
+ return false;
+}
+
+bool FormatEntity::Format(const Entry &entry, Stream &s,
+ const SymbolContext *sc,
+ const ExecutionContext *exe_ctx, const Address *addr,
+ ValueObject *valobj, bool function_changed,
+ bool initial_function) {
+ switch (entry.type) {
+ case Entry::Type::Invalid:
+ case Entry::Type::ParentNumber: // Only used for
+ // FormatEntity::Entry::Definition encoding
+ case Entry::Type::ParentString: // Only used for
+ // FormatEntity::Entry::Definition encoding
+ return false;
+ case Entry::Type::EscapeCode:
+ if (exe_ctx) {
+ if (Target *target = exe_ctx->GetTargetPtr()) {
+ Debugger &debugger = target->GetDebugger();
+ if (debugger.GetUseColor()) {
+ s.PutCString(entry.string);
+ }
+ }
+ }
+ // Always return true, so colors being disabled is transparent.
+ return true;
+
+ case Entry::Type::Root:
+ for (const auto &child : entry.children) {
+ if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed,
+ initial_function)) {
+ return false; // If any item of root fails, then the formatting fails
+ }
+ }
+ return true; // Only return true if all items succeeded
+
+ case Entry::Type::String:
+ s.PutCString(entry.string);
+ return true;
+
+ case Entry::Type::Scope: {
+ StreamString scope_stream;
+ bool success = false;
+ for (const auto &child : entry.children) {
+ success = Format(child, scope_stream, sc, exe_ctx, addr, valobj,
+ function_changed, initial_function);
+ if (!success)
+ break;
+ }
+ // Only if all items in a scope succeed, then do we print the output into
+ // the main stream
+ if (success)
+ s.Write(scope_stream.GetString().data(), scope_stream.GetString().size());
+ }
+ return true; // Scopes always successfully print themselves
+
+ case Entry::Type::Variable:
+ case Entry::Type::VariableSynthetic:
+ case Entry::Type::ScriptVariable:
+ case Entry::Type::ScriptVariableSynthetic:
+ return DumpValue(s, sc, exe_ctx, entry, valobj);
+
+ case Entry::Type::AddressFile:
+ case Entry::Type::AddressLoad:
+ case Entry::Type::AddressLoadOrFile:
+ return (addr != nullptr && addr->IsValid() &&
+ DumpAddress(s, sc, exe_ctx, *addr,
+ entry.type == Entry::Type::AddressLoadOrFile));
+
+ case Entry::Type::ProcessID:
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ const char *format = "%" PRIu64;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, process->GetID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ProcessFile:
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) {
+ Module *exe_module = process->GetTarget().GetExecutableModulePointer();
+ if (exe_module) {
+ if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number))
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ScriptProcess:
+ if (exe_ctx) {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, process,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::ThreadID:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *format = "0x%4.4" PRIx64;
+ if (!entry.printf_format.empty()) {
+ // Watch for the special "tid" format...
+ if (entry.printf_format == "tid") {
+ // TODO(zturner): Rather than hardcoding this to be platform
+ // specific, it should be controlled by a setting and the default
+ // value of the setting can be different depending on the platform.
+ Target &target = thread->GetProcess()->GetTarget();
+ ArchSpec arch(target.GetArchitecture());
+ llvm::Triple::OSType ostype = arch.IsValid()
+ ? arch.GetTriple().getOS()
+ : llvm::Triple::UnknownOS;
+ if ((ostype == llvm::Triple::FreeBSD) ||
+ (ostype == llvm::Triple::Linux) ||
+ (ostype == llvm::Triple::NetBSD)) {
+ format = "%" PRIu64;
+ }
+ } else {
+ format = entry.printf_format.c_str();
+ }
+ }
+ s.Printf(format, thread->GetID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadProtocolID:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *format = "0x%4.4" PRIx64;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, thread->GetProtocolID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadIndexID:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, thread->GetIndexID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadName:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *cstr = thread->GetName();
+ if (cstr && cstr[0]) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadQueue:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ const char *cstr = thread->GetQueueName();
+ if (cstr && cstr[0]) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadStopReason:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ const char *cstr = stop_info_sp->GetDescription();
+ if (cstr && cstr[0]) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadReturnValue:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ ValueObjectSP return_valobj_sp =
+ StopInfo::GetReturnValueObject(stop_info_sp);
+ if (return_valobj_sp) {
+ return_valobj_sp->Dump(s);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ThreadCompletedExpression:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp && stop_info_sp->IsValid()) {
+ ExpressionVariableSP expression_var_sp =
+ StopInfo::GetExpressionVariable(stop_info_sp);
+ if (expression_var_sp && expression_var_sp->GetValueObject()) {
+ expression_var_sp->GetValueObject()->Dump(s);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ScriptThread:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, thread,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::ThreadInfo:
+ if (exe_ctx) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread) {
+ StructuredData::ObjectSP object_sp = thread->GetExtendedInfo();
+ if (object_sp &&
+ object_sp->GetType() == eStructuredDataTypeDictionary) {
+ if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s))
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::TargetArch:
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ const ArchSpec &arch = target->GetArchitecture();
+ if (arch.IsValid()) {
+ s.PutCString(arch.GetArchitectureName());
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ScriptTarget:
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, target,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::ModuleFile:
+ if (sc) {
+ Module *module = sc->module_sp.get();
+ if (module) {
+ if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::File:
+ if (sc) {
+ CompileUnit *cu = sc->comp_unit;
+ if (cu) {
+ // CompileUnit is a FileSpec
+ if (DumpFile(s, *cu, (FileKind)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::Lang:
+ if (sc) {
+ CompileUnit *cu = sc->comp_unit;
+ if (cu) {
+ const char *lang_name =
+ Language::GetNameForLanguageType(cu->GetLanguage());
+ if (lang_name) {
+ s.PutCString(lang_name);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameIndex:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, frame->GetFrameIndex());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterPC:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ const Address &pc_addr = frame->GetFrameCodeAddress();
+ if (pc_addr.IsValid()) {
+ if (DumpAddress(s, sc, exe_ctx, pc_addr, false))
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterSP:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP,
+ (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterFP:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
+ (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameRegisterFlags:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameNoDebug:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ return !frame->HasDebugInformation();
+ }
+ }
+ return true;
+
+ case Entry::Type::FrameRegisterByName:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpRegister(s, frame, entry.string.c_str(),
+ (lldb::Format)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FrameIsArtificial: {
+ if (exe_ctx)
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->IsArtificial();
+ return false;
+ }
+
+ case Entry::Type::ScriptFrame:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame)
+ return RunScriptFormatKeyword(s, sc, exe_ctx, frame,
+ entry.string.c_str());
+ }
+ return false;
+
+ case Entry::Type::FunctionID:
+ if (sc) {
+ if (sc->function) {
+ s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID());
+ return true;
+ } else if (sc->symbol) {
+ s.Printf("symbol[%u]", sc->symbol->GetID());
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionDidChange:
+ return function_changed;
+
+ case Entry::Type::FunctionInitialFunction:
+ return initial_function;
+
+ case Entry::Type::FunctionName: {
+ Language *language_plugin = nullptr;
+ bool language_plugin_handled = false;
+ StreamString ss;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+ if (language_plugin) {
+ language_plugin_handled = language_plugin->GetFunctionDisplayName(
+ sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
+ }
+ if (language_plugin_handled) {
+ s << ss.GetString();
+ return true;
+ } else {
+ const char *name = nullptr;
+ if (sc->function)
+ name = sc->function->GetName().AsCString(nullptr);
+ else if (sc->symbol)
+ name = sc->symbol->GetName().AsCString(nullptr);
+ if (name) {
+ s.PutCString(name);
+
+ if (sc->block) {
+ Block *inline_block = sc->block->GetContainingInlinedBlock();
+ if (inline_block) {
+ const InlineFunctionInfo *inline_info =
+ sc->block->GetInlinedFunctionInfo();
+ if (inline_info) {
+ s.PutCString(" [inlined] ");
+ inline_info->GetName(sc->function->GetLanguage()).Dump(&s);
+ }
+ }
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionNameNoArgs: {
+ Language *language_plugin = nullptr;
+ bool language_plugin_handled = false;
+ StreamString ss;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+ if (language_plugin) {
+ language_plugin_handled = language_plugin->GetFunctionDisplayName(
+ sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
+ ss);
+ }
+ if (language_plugin_handled) {
+ s << ss.GetString();
+ return true;
+ } else {
+ ConstString name;
+ if (sc->function)
+ name = sc->function->GetNameNoArguments();
+ else if (sc->symbol)
+ name = sc->symbol->GetNameNoArguments();
+ if (name) {
+ s.PutCString(name.GetCString());
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionNameWithArgs: {
+ Language *language_plugin = nullptr;
+ bool language_plugin_handled = false;
+ StreamString ss;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+ if (language_plugin) {
+ language_plugin_handled = language_plugin->GetFunctionDisplayName(
+ sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss);
+ }
+ if (language_plugin_handled) {
+ s << ss.GetString();
+ return true;
+ } else {
+ // Print the function name with arguments in it
+ if (sc->function) {
+ ExecutionContextScope *exe_scope =
+ exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
+ const char *cstr = sc->function->GetName().AsCString(nullptr);
+ if (cstr) {
+ const InlineFunctionInfo *inline_info = nullptr;
+ VariableListSP variable_list_sp;
+ bool get_function_vars = true;
+ if (sc->block) {
+ Block *inline_block = sc->block->GetContainingInlinedBlock();
+
+ if (inline_block) {
+ get_function_vars = false;
+ inline_info = sc->block->GetInlinedFunctionInfo();
+ if (inline_info)
+ variable_list_sp = inline_block->GetBlockVariableList(true);
+ }
+ }
+
+ if (get_function_vars) {
+ variable_list_sp =
+ sc->function->GetBlock(true).GetBlockVariableList(true);
+ }
+
+ if (inline_info) {
+ s.PutCString(cstr);
+ s.PutCString(" [inlined] ");
+ cstr =
+ inline_info->GetName(sc->function->GetLanguage()).GetCString();
+ }
+
+ VariableList args;
+ if (variable_list_sp)
+ variable_list_sp->AppendVariablesWithScope(
+ eValueTypeVariableArgument, args);
+ if (args.GetSize() > 0) {
+ const char *open_paren = strchr(cstr, '(');
+ const char *close_paren = nullptr;
+ const char *generic = strchr(cstr, '<');
+ // if before the arguments list begins there is a template sign
+ // then scan to the end of the generic args before you try to find
+ // the arguments list
+ if (generic && open_paren && generic < open_paren) {
+ int generic_depth = 1;
+ ++generic;
+ for (; *generic && generic_depth > 0; generic++) {
+ if (*generic == '<')
+ generic_depth++;
+ if (*generic == '>')
+ generic_depth--;
+ }
+ if (*generic)
+ open_paren = strchr(generic, '(');
+ else
+ open_paren = nullptr;
+ }
+ if (open_paren) {
+ if (IsToken(open_paren, "(anonymous namespace)")) {
+ open_paren =
+ strchr(open_paren + strlen("(anonymous namespace)"), '(');
+ if (open_paren)
+ close_paren = strchr(open_paren, ')');
+ } else
+ close_paren = strchr(open_paren, ')');
+ }
+
+ if (open_paren)
+ s.Write(cstr, open_paren - cstr + 1);
+ else {
+ s.PutCString(cstr);
+ s.PutChar('(');
+ }
+ const size_t num_args = args.GetSize();
+ for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
+ std::string buffer;
+
+ VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
+ ValueObjectSP var_value_sp(
+ ValueObjectVariable::Create(exe_scope, var_sp));
+ StreamString ss;
+ llvm::StringRef var_representation;
+ const char *var_name = var_value_sp->GetName().GetCString();
+ if (var_value_sp->GetCompilerType().IsValid()) {
+ if (var_value_sp && exe_scope->CalculateTarget())
+ var_value_sp =
+ var_value_sp->GetQualifiedRepresentationIfAvailable(
+ exe_scope->CalculateTarget()
+ ->TargetProperties::GetPreferDynamicValue(),
+ exe_scope->CalculateTarget()
+ ->TargetProperties::GetEnableSyntheticValue());
+ if (var_value_sp->GetCompilerType().IsAggregateType() &&
+ DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) {
+ static StringSummaryFormat format(
+ TypeSummaryImpl::Flags()
+ .SetHideItemNames(false)
+ .SetShowMembersOneLiner(true),
+ "");
+ format.FormatObject(var_value_sp.get(), buffer,
+ TypeSummaryOptions());
+ var_representation = buffer;
+ } else
+ var_value_sp->DumpPrintableRepresentation(
+ ss,
+ ValueObject::ValueObjectRepresentationStyle::
+ eValueObjectRepresentationStyleSummary,
+ eFormatDefault,
+ ValueObject::PrintableRepresentationSpecialCases::eAllow,
+ false);
+ }
+
+ if (!ss.GetString().empty())
+ var_representation = ss.GetString();
+ if (arg_idx > 0)
+ s.PutCString(", ");
+ if (var_value_sp->GetError().Success()) {
+ if (!var_representation.empty())
+ s.Printf("%s=%s", var_name, var_representation.str().c_str());
+ else
+ s.Printf("%s=%s at %s", var_name,
+ var_value_sp->GetTypeName().GetCString(),
+ var_value_sp->GetLocationAsCString());
+ } else
+ s.Printf("%s=<unavailable>", var_name);
+ }
+
+ if (close_paren)
+ s.PutCString(close_paren);
+ else
+ s.PutChar(')');
+
+ } else {
+ s.PutCString(cstr);
+ }
+ return true;
+ }
+ } else if (sc->symbol) {
+ const char *cstr = sc->symbol->GetName().AsCString(nullptr);
+ if (cstr) {
+ s.PutCString(cstr);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionAddrOffset:
+ if (addr) {
+ if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false,
+ false))
+ return true;
+ }
+ return false;
+
+ case Entry::Type::FunctionAddrOffsetConcrete:
+ if (addr) {
+ if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true,
+ true))
+ return true;
+ }
+ return false;
+
+ case Entry::Type::FunctionLineOffset:
+ return (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
+ sc->line_entry.range.GetBaseAddress(),
+ false, false, false));
+
+ case Entry::Type::FunctionPCOffset:
+ if (exe_ctx) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
+ if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
+ frame->GetFrameCodeAddress(), false,
+ false, false))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::FunctionChanged:
+ return function_changed;
+
+ case Entry::Type::FunctionIsOptimized: {
+ bool is_optimized = false;
+ if (sc->function && sc->function->GetIsOptimized()) {
+ is_optimized = true;
+ }
+ return is_optimized;
+ }
+
+ case Entry::Type::FunctionInitial:
+ return initial_function;
+
+ case Entry::Type::LineEntryFile:
+ if (sc && sc->line_entry.IsValid()) {
+ Module *module = sc->module_sp.get();
+ if (module) {
+ if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number))
+ return true;
+ }
+ }
+ return false;
+
+ case Entry::Type::LineEntryLineNumber:
+ if (sc && sc->line_entry.IsValid()) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, sc->line_entry.line);
+ return true;
+ }
+ return false;
+
+ case Entry::Type::LineEntryColumn:
+ if (sc && sc->line_entry.IsValid() && sc->line_entry.column) {
+ const char *format = "%" PRIu32;
+ if (!entry.printf_format.empty())
+ format = entry.printf_format.c_str();
+ s.Printf(format, sc->line_entry.column);
+ return true;
+ }
+ return false;
+
+ case Entry::Type::LineEntryStartAddress:
+ case Entry::Type::LineEntryEndAddress:
+ if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) {
+ Address addr = sc->line_entry.range.GetBaseAddress();
+
+ if (entry.type == Entry::Type::LineEntryEndAddress)
+ addr.Slide(sc->line_entry.range.GetByteSize());
+ if (DumpAddress(s, sc, exe_ctx, addr, false))
+ return true;
+ }
+ return false;
+
+ case Entry::Type::CurrentPCArrow:
+ if (addr && exe_ctx && exe_ctx->GetFramePtr()) {
+ RegisterContextSP reg_ctx =
+ exe_ctx->GetFramePtr()->GetRegisterContextSP();
+ if (reg_ctx) {
+ addr_t pc_loadaddr = reg_ctx->GetPC();
+ if (pc_loadaddr != LLDB_INVALID_ADDRESS) {
+ Address pc;
+ pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr());
+ if (pc == *addr) {
+ s.Printf("-> ");
+ return true;
+ }
+ }
+ }
+ s.Printf(" ");
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
+static bool DumpCommaSeparatedChildEntryNames(
+ Stream &s, const FormatEntity::Entry::Definition *parent) {
+ if (parent->children) {
+ const size_t n = parent->num_children;
+ for (size_t i = 0; i < n; ++i) {
+ if (i > 0)
+ s.PutCString(", ");
+ s.Printf("\"%s\"", parent->children[i].name);
+ }
+ return true;
+ }
+ return false;
+}
+
+static Status ParseEntry(const llvm::StringRef &format_str,
+ const FormatEntity::Entry::Definition *parent,
+ FormatEntity::Entry &entry) {
+ Status error;
+
+ const size_t sep_pos = format_str.find_first_of(".[:");
+ const char sep_char =
+ (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos];
+ llvm::StringRef key = format_str.substr(0, sep_pos);
+
+ const size_t n = parent->num_children;
+ for (size_t i = 0; i < n; ++i) {
+ const FormatEntity::Entry::Definition *entry_def = parent->children + i;
+ if (key.equals(entry_def->name) || entry_def->name[0] == '*') {
+ llvm::StringRef value;
+ if (sep_char)
+ value =
+ format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1));
+ switch (entry_def->type) {
+ case FormatEntity::Entry::Type::ParentString:
+ entry.string = format_str.str();
+ return error; // Success
+
+ case FormatEntity::Entry::Type::ParentNumber:
+ entry.number = entry_def->data;
+ return error; // Success
+
+ case FormatEntity::Entry::Type::EscapeCode:
+ entry.type = entry_def->type;
+ entry.string = entry_def->string;
+ return error; // Success
+
+ default:
+ entry.type = entry_def->type;
+ break;
+ }
+
+ if (value.empty()) {
+ if (entry_def->type == FormatEntity::Entry::Type::Invalid) {
+ if (entry_def->children) {
+ StreamString error_strm;
+ error_strm.Printf("'%s' can't be specified on its own, you must "
+ "access one of its children: ",
+ entry_def->name);
+ DumpCommaSeparatedChildEntryNames(error_strm, entry_def);
+ error.SetErrorStringWithFormat("%s", error_strm.GetData());
+ } else if (sep_char == ':') {
+ // Any value whose separator is a with a ':' means this value has a
+ // string argument that needs to be stored in the entry (like
+ // "${script.var:}"). In this case the string value is the empty
+ // string which is ok.
+ } else {
+ error.SetErrorStringWithFormat("%s", "invalid entry definitions");
+ }
+ }
+ } else {
+ if (entry_def->children) {
+ error = ParseEntry(value, entry_def, entry);
+ } else if (sep_char == ':') {
+ // Any value whose separator is a with a ':' means this value has a
+ // string argument that needs to be stored in the entry (like
+ // "${script.var:modulename.function}")
+ entry.string = value.str();
+ } else {
+ error.SetErrorStringWithFormat(
+ "'%s' followed by '%s' but it has no children", key.str().c_str(),
+ value.str().c_str());
+ }
+ }
+ return error;
+ }
+ }
+ StreamString error_strm;
+ if (parent->type == FormatEntity::Entry::Type::Root)
+ error_strm.Printf(
+ "invalid top level item '%s'. Valid top level items are: ",
+ key.str().c_str());
+ else
+ error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ",
+ key.str().c_str(), parent->name);
+ DumpCommaSeparatedChildEntryNames(error_strm, parent);
+ error.SetErrorStringWithFormat("%s", error_strm.GetData());
+ return error;
+}
+
+static const FormatEntity::Entry::Definition *
+FindEntry(const llvm::StringRef &format_str,
+ const FormatEntity::Entry::Definition *parent,
+ llvm::StringRef &remainder) {
+ Status error;
+
+ std::pair<llvm::StringRef, llvm::StringRef> p = format_str.split('.');
+ const size_t n = parent->num_children;
+ for (size_t i = 0; i < n; ++i) {
+ const FormatEntity::Entry::Definition *entry_def = parent->children + i;
+ if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') {
+ if (p.second.empty()) {
+ if (format_str.back() == '.')
+ remainder = format_str.drop_front(format_str.size() - 1);
+ else
+ remainder = llvm::StringRef(); // Exact match
+ return entry_def;
+ } else {
+ if (entry_def->children) {
+ return FindEntry(p.second, entry_def, remainder);
+ } else {
+ remainder = p.second;
+ return entry_def;
+ }
+ }
+ }
+ }
+ remainder = format_str;
+ return parent;
+}
+
+Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry,
+ uint32_t depth) {
+ Status error;
+ while (!format.empty() && error.Success()) {
+ const size_t non_special_chars = format.find_first_of("${}\\");
+
+ if (non_special_chars == llvm::StringRef::npos) {
+ // No special characters, just string bytes so add them and we are done
+ parent_entry.AppendText(format);
+ return error;
+ }
+
+ if (non_special_chars > 0) {
+ // We have a special character, so add all characters before these as a
+ // plain string
+ parent_entry.AppendText(format.substr(0, non_special_chars));
+ format = format.drop_front(non_special_chars);
+ }
+
+ switch (format[0]) {
+ case '\0':
+ return error;
+
+ case '{': {
+ format = format.drop_front(); // Skip the '{'
+ Entry scope_entry(Entry::Type::Scope);
+ error = FormatEntity::ParseInternal(format, scope_entry, depth + 1);
+ if (error.Fail())
+ return error;
+ parent_entry.AppendEntry(std::move(scope_entry));
+ } break;
+
+ case '}':
+ if (depth == 0)
+ error.SetErrorString("unmatched '}' character");
+ else
+ format =
+ format
+ .drop_front(); // Skip the '}' as we are at the end of the scope
+ return error;
+
+ case '\\': {
+ format = format.drop_front(); // Skip the '\' character
+ if (format.empty()) {
+ error.SetErrorString(
+ "'\\' character was not followed by another character");
+ return error;
+ }
+
+ const char desens_char = format[0];
+ format = format.drop_front(); // Skip the desensitized char character
+ switch (desens_char) {
+ case 'a':
+ parent_entry.AppendChar('\a');
+ break;
+ case 'b':
+ parent_entry.AppendChar('\b');
+ break;
+ case 'f':
+ parent_entry.AppendChar('\f');
+ break;
+ case 'n':
+ parent_entry.AppendChar('\n');
+ break;
+ case 'r':
+ parent_entry.AppendChar('\r');
+ break;
+ case 't':
+ parent_entry.AppendChar('\t');
+ break;
+ case 'v':
+ parent_entry.AppendChar('\v');
+ break;
+ case '\'':
+ parent_entry.AppendChar('\'');
+ break;
+ case '\\':
+ parent_entry.AppendChar('\\');
+ break;
+ case '0':
+ // 1 to 3 octal chars
+ {
+ // Make a string that can hold onto the initial zero char, up to 3
+ // octal digits, and a terminating NULL.
+ char oct_str[5] = {0, 0, 0, 0, 0};
+
+ int i;
+ for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i)
+ oct_str[i] = format[i];
+
+ // We don't want to consume the last octal character since the main
+ // for loop will do this for us, so we advance p by one less than i
+ // (even if i is zero)
+ format = format.drop_front(i);
+ unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
+ if (octal_value <= UINT8_MAX) {
+ parent_entry.AppendChar((char)octal_value);
+ } else {
+ error.SetErrorString("octal number is larger than a single byte");
+ return error;
+ }
+ }
+ break;
+
+ case 'x':
+ // hex number in the format
+ if (isxdigit(format[0])) {
+ // Make a string that can hold onto two hex chars plus a
+ // NULL terminator
+ char hex_str[3] = {0, 0, 0};
+ hex_str[0] = format[0];
+
+ format = format.drop_front();
+
+ if (isxdigit(format[0])) {
+ hex_str[1] = format[0];
+ format = format.drop_front();
+ }
+
+ unsigned long hex_value = strtoul(hex_str, nullptr, 16);
+ if (hex_value <= UINT8_MAX) {
+ parent_entry.AppendChar((char)hex_value);
+ } else {
+ error.SetErrorString("hex number is larger than a single byte");
+ return error;
+ }
+ } else {
+ parent_entry.AppendChar(desens_char);
+ }
+ break;
+
+ default:
+ // Just desensitize any other character by just printing what came
+ // after the '\'
+ parent_entry.AppendChar(desens_char);
+ break;
+ }
+ } break;
+
+ case '$':
+ if (format.size() == 1) {
+ // '$' at the end of a format string, just print the '$'
+ parent_entry.AppendText("$");
+ } else {
+ format = format.drop_front(); // Skip the '$'
+
+ if (format[0] == '{') {
+ format = format.drop_front(); // Skip the '{'
+
+ llvm::StringRef variable, variable_format;
+ error = FormatEntity::ExtractVariableInfo(format, variable,
+ variable_format);
+ if (error.Fail())
+ return error;
+ bool verify_is_thread_id = false;
+ Entry entry;
+ if (!variable_format.empty()) {
+ entry.printf_format = variable_format.str();
+
+ // If the format contains a '%' we are going to assume this is a
+ // printf style format. So if you want to format your thread ID
+ // using "0x%llx" you can use: ${thread.id%0x%llx}
+ //
+ // If there is no '%' in the format, then it is assumed to be a
+ // LLDB format name, or one of the extended formats specified in
+ // the switch statement below.
+
+ if (entry.printf_format.find('%') == std::string::npos) {
+ bool clear_printf = false;
+
+ if (FormatManager::GetFormatFromCString(
+ entry.printf_format.c_str(), false, entry.fmt)) {
+ // We have an LLDB format, so clear the printf format
+ clear_printf = true;
+ } else if (entry.printf_format.size() == 1) {
+ switch (entry.printf_format[0]) {
+ case '@': // if this is an @ sign, print ObjC description
+ entry.number = ValueObject::
+ eValueObjectRepresentationStyleLanguageSpecific;
+ clear_printf = true;
+ break;
+ case 'V': // if this is a V, print the value using the default
+ // format
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleValue;
+ clear_printf = true;
+ break;
+ case 'L': // if this is an L, print the location of the value
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleLocation;
+ clear_printf = true;
+ break;
+ case 'S': // if this is an S, print the summary after all
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleSummary;
+ clear_printf = true;
+ break;
+ case '#': // if this is a '#', print the number of children
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleChildrenCount;
+ clear_printf = true;
+ break;
+ case 'T': // if this is a 'T', print the type
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleType;
+ clear_printf = true;
+ break;
+ case 'N': // if this is a 'N', print the name
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleName;
+ clear_printf = true;
+ break;
+ case '>': // if this is a '>', print the expression path
+ entry.number = ValueObject::
+ eValueObjectRepresentationStyleExpressionPath;
+ clear_printf = true;
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid format: '%s'",
+ entry.printf_format.c_str());
+ return error;
+ }
+ } else if (FormatManager::GetFormatFromCString(
+ entry.printf_format.c_str(), true, entry.fmt)) {
+ clear_printf = true;
+ } else if (entry.printf_format == "tid") {
+ verify_is_thread_id = true;
+ } else {
+ error.SetErrorStringWithFormat("invalid format: '%s'",
+ entry.printf_format.c_str());
+ return error;
+ }
+
+ // Our format string turned out to not be a printf style format
+ // so lets clear the string
+ if (clear_printf)
+ entry.printf_format.clear();
+ }
+ }
+
+ // Check for dereferences
+ if (variable[0] == '*') {
+ entry.deref = true;
+ variable = variable.drop_front();
+ }
+
+ error = ParseEntry(variable, &g_root, entry);
+ if (error.Fail())
+ return error;
+
+ if (verify_is_thread_id) {
+ if (entry.type != Entry::Type::ThreadID &&
+ entry.type != Entry::Type::ThreadProtocolID) {
+ error.SetErrorString("the 'tid' format can only be used on "
+ "${thread.id} and ${thread.protocol_id}");
+ }
+ }
+
+ switch (entry.type) {
+ case Entry::Type::Variable:
+ case Entry::Type::VariableSynthetic:
+ if (entry.number == 0) {
+ if (entry.string.empty())
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleValue;
+ else
+ entry.number =
+ ValueObject::eValueObjectRepresentationStyleSummary;
+ }
+ break;
+ default:
+ // Make sure someone didn't try to dereference anything but ${var}
+ // or ${svar}
+ if (entry.deref) {
+ error.SetErrorStringWithFormat(
+ "${%s} can't be dereferenced, only ${var} and ${svar} can.",
+ variable.str().c_str());
+ return error;
+ }
+ }
+ parent_entry.AppendEntry(std::move(entry));
+ }
+ }
+ break;
+ }
+ }
+ return error;
+}
+
+Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str,
+ llvm::StringRef &variable_name,
+ llvm::StringRef &variable_format) {
+ Status error;
+ variable_name = llvm::StringRef();
+ variable_format = llvm::StringRef();
+
+ const size_t paren_pos = format_str.find('}');
+ if (paren_pos != llvm::StringRef::npos) {
+ const size_t percent_pos = format_str.find('%');
+ if (percent_pos < paren_pos) {
+ if (percent_pos > 0) {
+ if (percent_pos > 1)
+ variable_name = format_str.substr(0, percent_pos);
+ variable_format =
+ format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1));
+ }
+ } else {
+ variable_name = format_str.substr(0, paren_pos);
+ }
+ // Strip off elements and the formatting and the trailing '}'
+ format_str = format_str.substr(paren_pos + 1);
+ } else {
+ error.SetErrorStringWithFormat(
+ "missing terminating '}' character for '${%s'",
+ format_str.str().c_str());
+ }
+ return error;
+}
+
+bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s,
+ llvm::StringRef variable_name,
+ llvm::StringRef variable_format) {
+ if (variable_name.empty() || variable_name.equals(".fullpath")) {
+ file_spec.Dump(&s);
+ return true;
+ } else if (variable_name.equals(".basename")) {
+ s.PutCString(file_spec.GetFilename().AsCString(""));
+ return true;
+ } else if (variable_name.equals(".dirname")) {
+ s.PutCString(file_spec.GetFilename().AsCString(""));
+ return true;
+ }
+ return false;
+}
+
+static std::string MakeMatch(const llvm::StringRef &prefix,
+ const char *suffix) {
+ std::string match(prefix.str());
+ match.append(suffix);
+ return match;
+}
+
+static void AddMatches(const FormatEntity::Entry::Definition *def,
+ const llvm::StringRef &prefix,
+ const llvm::StringRef &match_prefix,
+ StringList &matches) {
+ const size_t n = def->num_children;
+ if (n > 0) {
+ for (size_t i = 0; i < n; ++i) {
+ std::string match = prefix.str();
+ if (match_prefix.empty())
+ matches.AppendString(MakeMatch(prefix, def->children[i].name));
+ else if (strncmp(def->children[i].name, match_prefix.data(),
+ match_prefix.size()) == 0)
+ matches.AppendString(
+ MakeMatch(prefix, def->children[i].name + match_prefix.size()));
+ }
+ }
+}
+
+void FormatEntity::AutoComplete(CompletionRequest &request) {
+ llvm::StringRef str = request.GetCursorArgumentPrefix();
+
+ const size_t dollar_pos = str.rfind('$');
+ if (dollar_pos == llvm::StringRef::npos)
+ return;
+
+ // Hitting TAB after $ at the end of the string add a "{"
+ if (dollar_pos == str.size() - 1) {
+ std::string match = str.str();
+ match.append("{");
+ request.AddCompletion(match);
+ return;
+ }
+
+ if (str[dollar_pos + 1] != '{')
+ return;
+
+ const size_t close_pos = str.find('}', dollar_pos + 2);
+ if (close_pos != llvm::StringRef::npos)
+ return;
+
+ const size_t format_pos = str.find('%', dollar_pos + 2);
+ if (format_pos != llvm::StringRef::npos)
+ return;
+
+ llvm::StringRef partial_variable(str.substr(dollar_pos + 2));
+ if (partial_variable.empty()) {
+ // Suggest all top level entites as we are just past "${"
+ StringList new_matches;
+ AddMatches(&g_root, str, llvm::StringRef(), new_matches);
+ request.AddCompletions(new_matches);
+ return;
+ }
+
+ // We have a partially specified variable, find it
+ llvm::StringRef remainder;
+ const FormatEntity::Entry::Definition *entry_def =
+ FindEntry(partial_variable, &g_root, remainder);
+ if (!entry_def)
+ return;
+
+ const size_t n = entry_def->num_children;
+
+ if (remainder.empty()) {
+ // Exact match
+ if (n > 0) {
+ // "${thread.info" <TAB>
+ request.AddCompletion(MakeMatch(str, "."));
+ } else {
+ // "${thread.id" <TAB>
+ request.AddCompletion(MakeMatch(str, "}"));
+ }
+ } else if (remainder.equals(".")) {
+ // "${thread." <TAB>
+ StringList new_matches;
+ AddMatches(entry_def, str, llvm::StringRef(), new_matches);
+ request.AddCompletions(new_matches);
+ } else {
+ // We have a partial match
+ // "${thre" <TAB>
+ StringList new_matches;
+ AddMatches(entry_def, str, remainder, new_matches);
+ request.AddCompletions(new_matches);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Highlighter.cpp b/contrib/llvm-project/lldb/source/Core/Highlighter.cpp
new file mode 100644
index 000000000000..c3c614aac210
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Highlighter.cpp
@@ -0,0 +1,81 @@
+//===-- Highlighter.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Highlighter.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+using namespace lldb_private::ansi;
+
+void HighlightStyle::ColorStyle::Apply(Stream &s, llvm::StringRef value) const {
+ s << m_prefix << value << m_suffix;
+}
+
+void HighlightStyle::ColorStyle::Set(llvm::StringRef prefix,
+ llvm::StringRef suffix) {
+ m_prefix = FormatAnsiTerminalCodes(prefix);
+ m_suffix = FormatAnsiTerminalCodes(suffix);
+}
+
+void DefaultHighlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines,
+ Stream &s) const {
+ // If we don't have a valid cursor, then we just print the line as-is.
+ if (!cursor_pos || *cursor_pos >= line.size()) {
+ s << line;
+ return;
+ }
+
+ // If we have a valid cursor, we have to apply the 'selected' style around
+ // the character below the cursor.
+
+ // Split the line around the character which is below the cursor.
+ size_t column = *cursor_pos;
+ // Print the characters before the cursor.
+ s << line.substr(0, column);
+ // Print the selected character with the defined color codes.
+ options.selected.Apply(s, line.substr(column, 1));
+ // Print the rest of the line.
+ s << line.substr(column + 1U);
+}
+
+static HighlightStyle::ColorStyle GetColor(const char *c) {
+ return HighlightStyle::ColorStyle(c, "${ansi.normal}");
+}
+
+HighlightStyle HighlightStyle::MakeVimStyle() {
+ HighlightStyle result;
+ result.comment = GetColor("${ansi.fg.purple}");
+ result.scalar_literal = GetColor("${ansi.fg.red}");
+ result.keyword = GetColor("${ansi.fg.green}");
+ return result;
+}
+
+const Highlighter &
+HighlighterManager::getHighlighterFor(lldb::LanguageType language_type,
+ llvm::StringRef path) const {
+ Language *language = lldb_private::Language::FindPlugin(language_type, path);
+ if (language && language->GetHighlighter())
+ return *language->GetHighlighter();
+ return m_default;
+}
+
+std::string Highlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::Optional<size_t> cursor_pos,
+ llvm::StringRef previous_lines) const {
+ StreamString s;
+ Highlight(options, line, cursor_pos, previous_lines, s);
+ s.Flush();
+ return s.GetString().str();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/IOHandler.cpp b/contrib/llvm-project/lldb/source/Core/IOHandler.cpp
new file mode 100644
index 000000000000..46be29f6fbf9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/IOHandler.cpp
@@ -0,0 +1,4625 @@
+//===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/IOHandler.h"
+
+#ifndef LLDB_DISABLE_CURSES
+#include <curses.h>
+#include <panel.h>
+#endif
+
+#if defined(__APPLE__)
+#include <deque>
+#endif
+#include <string>
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/File.h"
+#include "lldb/Utility/Predicate.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/lldb-forward.h"
+
+#ifndef LLDB_DISABLE_LIBEDIT
+#include "lldb/Host/Editline.h"
+#endif
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#ifndef LLDB_DISABLE_CURSES
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectRegister.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/State.h"
+#endif
+
+#include "llvm/ADT/StringRef.h"
+
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#endif
+
+#include <memory>
+#include <mutex>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <type_traits>
+
+using namespace lldb;
+using namespace lldb_private;
+using llvm::None;
+using llvm::Optional;
+using llvm::StringRef;
+
+
+IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
+ : IOHandler(debugger, type,
+ FileSP(), // Adopt STDIN from top input reader
+ StreamFileSP(), // Adopt STDOUT from top input reader
+ StreamFileSP(), // Adopt STDERR from top input reader
+ 0, // Flags
+ nullptr // Shadow file recorder
+ ) {}
+
+IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type,
+ const lldb::FileSP &input_sp,
+ const lldb::StreamFileSP &output_sp,
+ const lldb::StreamFileSP &error_sp, uint32_t flags,
+ repro::DataRecorder *data_recorder)
+ : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
+ m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false),
+ m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false),
+ m_active(false) {
+ // If any files are not specified, then adopt them from the top input reader.
+ if (!m_input_sp || !m_output_sp || !m_error_sp)
+ debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp,
+ m_error_sp);
+}
+
+IOHandler::~IOHandler() = default;
+
+int IOHandler::GetInputFD() {
+ return (m_input_sp ? m_input_sp->GetDescriptor() : -1);
+}
+
+int IOHandler::GetOutputFD() {
+ return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
+}
+
+int IOHandler::GetErrorFD() {
+ return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
+}
+
+FILE *IOHandler::GetInputFILE() {
+ return (m_input_sp ? m_input_sp->GetStream() : nullptr);
+}
+
+FILE *IOHandler::GetOutputFILE() {
+ return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
+}
+
+FILE *IOHandler::GetErrorFILE() {
+ return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
+}
+
+FileSP &IOHandler::GetInputFileSP() { return m_input_sp; }
+
+StreamFileSP &IOHandler::GetOutputStreamFileSP() { return m_output_sp; }
+
+StreamFileSP &IOHandler::GetErrorStreamFileSP() { return m_error_sp; }
+
+bool IOHandler::GetIsInteractive() {
+ return GetInputFileSP() ? GetInputFileSP()->GetIsInteractive() : false;
+}
+
+bool IOHandler::GetIsRealTerminal() {
+ return GetInputFileSP() ? GetInputFileSP()->GetIsRealTerminal() : false;
+}
+
+void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); }
+
+void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); }
+
+void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
+ if (stream) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (m_top)
+ m_top->PrintAsync(stream, s, len);
+ }
+}
+
+IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
+ bool default_response)
+ : IOHandlerEditline(
+ debugger, IOHandler::Type::Confirm,
+ nullptr, // nullptr editline_name means no history loaded/saved
+ llvm::StringRef(), // No prompt
+ llvm::StringRef(), // No continuation prompt
+ false, // Multi-line
+ false, // Don't colorize the prompt (i.e. the confirm message.)
+ 0, *this, nullptr),
+ m_default_response(default_response), m_user_response(default_response) {
+ StreamString prompt_stream;
+ prompt_stream.PutCString(prompt);
+ if (m_default_response)
+ prompt_stream.Printf(": [Y/n] ");
+ else
+ prompt_stream.Printf(": [y/N] ");
+
+ SetPrompt(prompt_stream.GetString());
+}
+
+IOHandlerConfirm::~IOHandlerConfirm() = default;
+
+void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
+ CompletionRequest &request) {
+ if (request.GetRawCursorPos() != 0)
+ return;
+ request.AddCompletion(m_default_response ? "y" : "n");
+}
+
+void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &line) {
+ if (line.empty()) {
+ // User just hit enter, set the response to the default
+ m_user_response = m_default_response;
+ io_handler.SetIsDone(true);
+ return;
+ }
+
+ if (line.size() == 1) {
+ switch (line[0]) {
+ case 'y':
+ case 'Y':
+ m_user_response = true;
+ io_handler.SetIsDone(true);
+ return;
+ case 'n':
+ case 'N':
+ m_user_response = false;
+ io_handler.SetIsDone(true);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (line == "yes" || line == "YES" || line == "Yes") {
+ m_user_response = true;
+ io_handler.SetIsDone(true);
+ } else if (line == "no" || line == "NO" || line == "No") {
+ m_user_response = false;
+ io_handler.SetIsDone(true);
+ }
+}
+
+void IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler,
+ CompletionRequest &request) {
+ switch (m_completion) {
+ case Completion::None:
+ break;
+ case Completion::LLDBCommand:
+ io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(request);
+ break;
+ case Completion::Expression:
+ CommandCompletions::InvokeCommonCompletionCallbacks(
+ io_handler.GetDebugger().GetCommandInterpreter(),
+ CommandCompletions::eVariablePathCompletion, request, nullptr);
+ break;
+ }
+}
+
+IOHandlerEditline::IOHandlerEditline(
+ Debugger &debugger, IOHandler::Type type,
+ const char *editline_name, // Used for saving history files
+ llvm::StringRef prompt, llvm::StringRef continuation_prompt,
+ bool multi_line, bool color_prompts, uint32_t line_number_start,
+ IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
+ : IOHandlerEditline(debugger, type,
+ FileSP(), // Inherit input from top input reader
+ StreamFileSP(), // Inherit output from top input reader
+ StreamFileSP(), // Inherit error from top input reader
+ 0, // Flags
+ editline_name, // Used for saving history files
+ prompt, continuation_prompt, multi_line, color_prompts,
+ line_number_start, delegate, data_recorder) {}
+
+IOHandlerEditline::IOHandlerEditline(
+ Debugger &debugger, IOHandler::Type type, const lldb::FileSP &input_sp,
+ const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp,
+ uint32_t flags,
+ const char *editline_name, // Used for saving history files
+ llvm::StringRef prompt, llvm::StringRef continuation_prompt,
+ bool multi_line, bool color_prompts, uint32_t line_number_start,
+ IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
+ : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags,
+ data_recorder),
+#ifndef LLDB_DISABLE_LIBEDIT
+ m_editline_up(),
+#endif
+ m_delegate(delegate), m_prompt(), m_continuation_prompt(),
+ m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
+ m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line),
+ m_color_prompts(color_prompts), m_interrupt_exits(true),
+ m_editing(false) {
+ SetPrompt(prompt);
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ bool use_editline = false;
+
+ use_editline = GetInputFILE() && GetOutputFILE() && GetErrorFILE() &&
+ m_input_sp && m_input_sp->GetIsRealTerminal();
+
+ if (use_editline) {
+ m_editline_up.reset(new Editline(editline_name, GetInputFILE(),
+ GetOutputFILE(), GetErrorFILE(),
+ m_color_prompts));
+ m_editline_up->SetIsInputCompleteCallback(IsInputCompleteCallback, this);
+ m_editline_up->SetAutoCompleteCallback(AutoCompleteCallback, this);
+ // See if the delegate supports fixing indentation
+ const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
+ if (indent_chars) {
+ // The delegate does support indentation, hook it up so when any
+ // indentation character is typed, the delegate gets a chance to fix it
+ m_editline_up->SetFixIndentationCallback(FixIndentationCallback, this,
+ indent_chars);
+ }
+ }
+#endif
+ SetBaseLineNumber(m_base_line_number);
+ SetPrompt(prompt);
+ SetContinuationPrompt(continuation_prompt);
+}
+
+IOHandlerEditline::~IOHandlerEditline() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ m_editline_up.reset();
+#endif
+}
+
+void IOHandlerEditline::Activate() {
+ IOHandler::Activate();
+ m_delegate.IOHandlerActivated(*this, GetIsInteractive());
+}
+
+void IOHandlerEditline::Deactivate() {
+ IOHandler::Deactivate();
+ m_delegate.IOHandlerDeactivated(*this);
+}
+
+// Split out a line from the buffer, if there is a full one to get.
+static Optional<std::string> SplitLine(std::string &line_buffer) {
+ size_t pos = line_buffer.find('\n');
+ if (pos == std::string::npos)
+ return None;
+ std::string line = StringRef(line_buffer.c_str(), pos).rtrim("\n\r");
+ line_buffer = line_buffer.substr(pos + 1);
+ return line;
+}
+
+// If the final line of the file ends without a end-of-line, return
+// it as a line anyway.
+static Optional<std::string> SplitLineEOF(std::string &line_buffer) {
+ if (llvm::all_of(line_buffer, isspace))
+ return None;
+ std::string line = std::move(line_buffer);
+ line_buffer.clear();
+ return line;
+}
+
+bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up) {
+ bool b = m_editline_up->GetLine(line, interrupted);
+ if (b && m_data_recorder)
+ m_data_recorder->Record(line, true);
+ return b;
+ }
+#endif
+
+ line.clear();
+
+ if (GetIsInteractive()) {
+ const char *prompt = nullptr;
+
+ if (m_multi_line && m_curr_line_idx > 0)
+ prompt = GetContinuationPrompt();
+
+ if (prompt == nullptr)
+ prompt = GetPrompt();
+
+ if (prompt && prompt[0]) {
+ if (m_output_sp) {
+ m_output_sp->Printf("%s", prompt);
+ m_output_sp->Flush();
+ }
+ }
+ }
+
+ Optional<std::string> got_line = SplitLine(m_line_buffer);
+
+ if (!got_line && !m_input_sp) {
+ // No more input file, we are done...
+ SetIsDone(true);
+ return false;
+ }
+
+ FILE *in = GetInputFILE();
+ char buffer[256];
+
+ if (!got_line && !in && m_input_sp) {
+ // there is no FILE*, fall back on just reading bytes from the stream.
+ while (!got_line) {
+ size_t bytes_read = sizeof(buffer);
+ Status error = m_input_sp->Read((void *)buffer, bytes_read);
+ if (error.Success() && !bytes_read) {
+ got_line = SplitLineEOF(m_line_buffer);
+ break;
+ }
+ if (error.Fail())
+ break;
+ m_line_buffer += StringRef(buffer, bytes_read);
+ got_line = SplitLine(m_line_buffer);
+ }
+ }
+
+ if (!got_line && in) {
+ m_editing = true;
+ while (!got_line) {
+ char *r = fgets(buffer, sizeof(buffer), in);
+#ifdef _WIN32
+ // ReadFile on Windows is supposed to set ERROR_OPERATION_ABORTED
+ // according to the docs on MSDN. However, this has evidently been a
+ // known bug since Windows 8. Therefore, we can't detect if a signal
+ // interrupted in the fgets. So pressing ctrl-c causes the repl to end
+ // and the process to exit. A temporary workaround is just to attempt to
+ // fgets twice until this bug is fixed.
+ if (r == nullptr)
+ r = fgets(buffer, sizeof(buffer), in);
+ // this is the equivalent of EINTR for Windows
+ if (r == nullptr && GetLastError() == ERROR_OPERATION_ABORTED)
+ continue;
+#endif
+ if (r == nullptr) {
+ if (ferror(in) && errno == EINTR)
+ continue;
+ if (feof(in))
+ got_line = SplitLineEOF(m_line_buffer);
+ break;
+ }
+ m_line_buffer += buffer;
+ got_line = SplitLine(m_line_buffer);
+ }
+ m_editing = false;
+ }
+
+ if (got_line) {
+ line = got_line.getValue();
+ if (m_data_recorder)
+ m_data_recorder->Record(line, true);
+ }
+
+ return (bool)got_line;
+}
+
+#ifndef LLDB_DISABLE_LIBEDIT
+bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
+ StringList &lines,
+ void *baton) {
+ IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
+ return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader,
+ lines);
+}
+
+int IOHandlerEditline::FixIndentationCallback(Editline *editline,
+ const StringList &lines,
+ int cursor_position,
+ void *baton) {
+ IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
+ return editline_reader->m_delegate.IOHandlerFixIndentation(
+ *editline_reader, lines, cursor_position);
+}
+
+void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request,
+ void *baton) {
+ IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
+ if (editline_reader)
+ editline_reader->m_delegate.IOHandlerComplete(*editline_reader, request);
+}
+#endif
+
+const char *IOHandlerEditline::GetPrompt() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up) {
+ return m_editline_up->GetPrompt();
+ } else {
+#endif
+ if (m_prompt.empty())
+ return nullptr;
+#ifndef LLDB_DISABLE_LIBEDIT
+ }
+#endif
+ return m_prompt.c_str();
+}
+
+bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
+ m_prompt = prompt;
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
+#endif
+ return true;
+}
+
+const char *IOHandlerEditline::GetContinuationPrompt() {
+ return (m_continuation_prompt.empty() ? nullptr
+ : m_continuation_prompt.c_str());
+}
+
+void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
+ m_continuation_prompt = prompt;
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
+ ? nullptr
+ : m_continuation_prompt.c_str());
+#endif
+}
+
+void IOHandlerEditline::SetBaseLineNumber(uint32_t line) {
+ m_base_line_number = line;
+}
+
+uint32_t IOHandlerEditline::GetCurrentLineIndex() const {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ return m_editline_up->GetCurrentLine();
+#endif
+ return m_curr_line_idx;
+}
+
+bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
+ m_current_lines_ptr = &lines;
+
+ bool success = false;
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up) {
+ return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
+ } else {
+#endif
+ bool done = false;
+ Status error;
+
+ while (!done) {
+ // Show line numbers if we are asked to
+ std::string line;
+ if (m_base_line_number > 0 && GetIsInteractive()) {
+ if (m_output_sp) {
+ m_output_sp->Printf("%u%s",
+ m_base_line_number + (uint32_t)lines.GetSize(),
+ GetPrompt() == nullptr ? " " : "");
+ }
+ }
+
+ m_curr_line_idx = lines.GetSize();
+
+ bool interrupted = false;
+ if (GetLine(line, interrupted) && !interrupted) {
+ lines.AppendString(line);
+ done = m_delegate.IOHandlerIsInputComplete(*this, lines);
+ } else {
+ done = true;
+ }
+ }
+ success = lines.GetSize() > 0;
+#ifndef LLDB_DISABLE_LIBEDIT
+ }
+#endif
+ return success;
+}
+
+// Each IOHandler gets to run until it is done. It should read data from the
+// "in" and place output into "out" and "err and return when done.
+void IOHandlerEditline::Run() {
+ std::string line;
+ while (IsActive()) {
+ bool interrupted = false;
+ if (m_multi_line) {
+ StringList lines;
+ if (GetLines(lines, interrupted)) {
+ if (interrupted) {
+ m_done = m_interrupt_exits;
+ m_delegate.IOHandlerInputInterrupted(*this, line);
+
+ } else {
+ line = lines.CopyList();
+ m_delegate.IOHandlerInputComplete(*this, line);
+ }
+ } else {
+ m_done = true;
+ }
+ } else {
+ if (GetLine(line, interrupted)) {
+ if (interrupted)
+ m_delegate.IOHandlerInputInterrupted(*this, line);
+ else
+ m_delegate.IOHandlerInputComplete(*this, line);
+ } else {
+ m_done = true;
+ }
+ }
+ }
+}
+
+void IOHandlerEditline::Cancel() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->Cancel();
+#endif
+}
+
+bool IOHandlerEditline::Interrupt() {
+ // Let the delgate handle it first
+ if (m_delegate.IOHandlerInterrupt(*this))
+ return true;
+
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ return m_editline_up->Interrupt();
+#endif
+ return false;
+}
+
+void IOHandlerEditline::GotEOF() {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->Interrupt();
+#endif
+}
+
+void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
+#ifndef LLDB_DISABLE_LIBEDIT
+ if (m_editline_up)
+ m_editline_up->PrintAsync(stream, s, len);
+ else
+#endif
+ {
+#ifdef _WIN32
+ const char *prompt = GetPrompt();
+ if (prompt) {
+ // Back up over previous prompt using Windows API
+ CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+ HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
+ COORD coord = screen_buffer_info.dwCursorPosition;
+ coord.X -= strlen(prompt);
+ if (coord.X < 0)
+ coord.X = 0;
+ SetConsoleCursorPosition(console_handle, coord);
+ }
+#endif
+ IOHandler::PrintAsync(stream, s, len);
+#ifdef _WIN32
+ if (prompt)
+ IOHandler::PrintAsync(GetOutputStreamFileSP().get(), prompt,
+ strlen(prompt));
+#endif
+ }
+}
+
+// we may want curses to be disabled for some builds for instance, windows
+#ifndef LLDB_DISABLE_CURSES
+
+#define KEY_RETURN 10
+#define KEY_ESCAPE 27
+
+namespace curses {
+class Menu;
+class MenuDelegate;
+class Window;
+class WindowDelegate;
+typedef std::shared_ptr<Menu> MenuSP;
+typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
+typedef std::shared_ptr<Window> WindowSP;
+typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
+typedef std::vector<MenuSP> Menus;
+typedef std::vector<WindowSP> Windows;
+typedef std::vector<WindowDelegateSP> WindowDelegates;
+
+#if 0
+type summary add -s "x=${var.x}, y=${var.y}" curses::Point
+type summary add -s "w=${var.width}, h=${var.height}" curses::Size
+type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
+#endif
+
+struct Point {
+ int x;
+ int y;
+
+ Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
+
+ void Clear() {
+ x = 0;
+ y = 0;
+ }
+
+ Point &operator+=(const Point &rhs) {
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+ }
+
+ void Dump() { printf("(x=%i, y=%i)\n", x, y); }
+};
+
+bool operator==(const Point &lhs, const Point &rhs) {
+ return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+bool operator!=(const Point &lhs, const Point &rhs) {
+ return lhs.x != rhs.x || lhs.y != rhs.y;
+}
+
+struct Size {
+ int width;
+ int height;
+ Size(int w = 0, int h = 0) : width(w), height(h) {}
+
+ void Clear() {
+ width = 0;
+ height = 0;
+ }
+
+ void Dump() { printf("(w=%i, h=%i)\n", width, height); }
+};
+
+bool operator==(const Size &lhs, const Size &rhs) {
+ return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+bool operator!=(const Size &lhs, const Size &rhs) {
+ return lhs.width != rhs.width || lhs.height != rhs.height;
+}
+
+struct Rect {
+ Point origin;
+ Size size;
+
+ Rect() : origin(), size() {}
+
+ Rect(const Point &p, const Size &s) : origin(p), size(s) {}
+
+ void Clear() {
+ origin.Clear();
+ size.Clear();
+ }
+
+ void Dump() {
+ printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
+ size.height);
+ }
+
+ void Inset(int w, int h) {
+ if (size.width > w * 2)
+ size.width -= w * 2;
+ origin.x += w;
+
+ if (size.height > h * 2)
+ size.height -= h * 2;
+ origin.y += h;
+ }
+
+ // Return a status bar rectangle which is the last line of this rectangle.
+ // This rectangle will be modified to not include the status bar area.
+ Rect MakeStatusBar() {
+ Rect status_bar;
+ if (size.height > 1) {
+ status_bar.origin.x = origin.x;
+ status_bar.origin.y = size.height;
+ status_bar.size.width = size.width;
+ status_bar.size.height = 1;
+ --size.height;
+ }
+ return status_bar;
+ }
+
+ // Return a menubar rectangle which is the first line of this rectangle. This
+ // rectangle will be modified to not include the menubar area.
+ Rect MakeMenuBar() {
+ Rect menubar;
+ if (size.height > 1) {
+ menubar.origin.x = origin.x;
+ menubar.origin.y = origin.y;
+ menubar.size.width = size.width;
+ menubar.size.height = 1;
+ ++origin.y;
+ --size.height;
+ }
+ return menubar;
+ }
+
+ void HorizontalSplitPercentage(float top_percentage, Rect &top,
+ Rect &bottom) const {
+ float top_height = top_percentage * size.height;
+ HorizontalSplit(top_height, top, bottom);
+ }
+
+ void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
+ top = *this;
+ if (top_height < size.height) {
+ top.size.height = top_height;
+ bottom.origin.x = origin.x;
+ bottom.origin.y = origin.y + top.size.height;
+ bottom.size.width = size.width;
+ bottom.size.height = size.height - top.size.height;
+ } else {
+ bottom.Clear();
+ }
+ }
+
+ void VerticalSplitPercentage(float left_percentage, Rect &left,
+ Rect &right) const {
+ float left_width = left_percentage * size.width;
+ VerticalSplit(left_width, left, right);
+ }
+
+ void VerticalSplit(int left_width, Rect &left, Rect &right) const {
+ left = *this;
+ if (left_width < size.width) {
+ left.size.width = left_width;
+ right.origin.x = origin.x + left.size.width;
+ right.origin.y = origin.y;
+ right.size.width = size.width - left.size.width;
+ right.size.height = size.height;
+ } else {
+ right.Clear();
+ }
+ }
+};
+
+bool operator==(const Rect &lhs, const Rect &rhs) {
+ return lhs.origin == rhs.origin && lhs.size == rhs.size;
+}
+
+bool operator!=(const Rect &lhs, const Rect &rhs) {
+ return lhs.origin != rhs.origin || lhs.size != rhs.size;
+}
+
+enum HandleCharResult {
+ eKeyNotHandled = 0,
+ eKeyHandled = 1,
+ eQuitApplication = 2
+};
+
+enum class MenuActionResult {
+ Handled,
+ NotHandled,
+ Quit // Exit all menus and quit
+};
+
+struct KeyHelp {
+ int ch;
+ const char *description;
+};
+
+class WindowDelegate {
+public:
+ virtual ~WindowDelegate() = default;
+
+ virtual bool WindowDelegateDraw(Window &window, bool force) {
+ return false; // Drawing not handled
+ }
+
+ virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) {
+ return eKeyNotHandled;
+ }
+
+ virtual const char *WindowDelegateGetHelpText() { return nullptr; }
+
+ virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
+};
+
+class HelpDialogDelegate : public WindowDelegate {
+public:
+ HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
+
+ ~HelpDialogDelegate() override;
+
+ bool WindowDelegateDraw(Window &window, bool force) override;
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
+
+ size_t GetNumLines() const { return m_text.GetSize(); }
+
+ size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
+
+protected:
+ StringList m_text;
+ int m_first_visible_line;
+};
+
+class Window {
+public:
+ Window(const char *name)
+ : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
+ m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
+ m_prev_active_window_idx(UINT32_MAX), m_delete(false),
+ m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
+
+ Window(const char *name, WINDOW *w, bool del = true)
+ : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
+ m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
+ m_prev_active_window_idx(UINT32_MAX), m_delete(del),
+ m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
+ if (w)
+ Reset(w);
+ }
+
+ Window(const char *name, const Rect &bounds)
+ : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
+ m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
+ m_prev_active_window_idx(UINT32_MAX), m_delete(true),
+ m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
+ Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
+ bounds.origin.y));
+ }
+
+ virtual ~Window() {
+ RemoveSubWindows();
+ Reset();
+ }
+
+ void Reset(WINDOW *w = nullptr, bool del = true) {
+ if (m_window == w)
+ return;
+
+ if (m_panel) {
+ ::del_panel(m_panel);
+ m_panel = nullptr;
+ }
+ if (m_window && m_delete) {
+ ::delwin(m_window);
+ m_window = nullptr;
+ m_delete = false;
+ }
+ if (w) {
+ m_window = w;
+ m_panel = ::new_panel(m_window);
+ m_delete = del;
+ }
+ }
+
+ void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
+ void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
+ void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
+ ::box(m_window, v_char, h_char);
+ }
+ void Clear() { ::wclear(m_window); }
+ void Erase() { ::werase(m_window); }
+ Rect GetBounds() {
+ return Rect(GetParentOrigin(), GetSize());
+ } // Get the rectangle in our parent window
+ int GetChar() { return ::wgetch(m_window); }
+ int GetCursorX() { return getcurx(m_window); }
+ int GetCursorY() { return getcury(m_window); }
+ Rect GetFrame() {
+ return Rect(Point(), GetSize());
+ } // Get our rectangle in our own coordinate system
+ Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
+ Size GetSize() { return Size(GetWidth(), GetHeight()); }
+ int GetParentX() { return getparx(m_window); }
+ int GetParentY() { return getpary(m_window); }
+ int GetMaxX() { return getmaxx(m_window); }
+ int GetMaxY() { return getmaxy(m_window); }
+ int GetWidth() { return GetMaxX(); }
+ int GetHeight() { return GetMaxY(); }
+ void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
+ void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
+ void Resize(int w, int h) { ::wresize(m_window, h, w); }
+ void Resize(const Size &size) {
+ ::wresize(m_window, size.height, size.width);
+ }
+ void PutChar(int ch) { ::waddch(m_window, ch); }
+ void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
+ void SetBackground(int color_pair_idx) {
+ ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
+ }
+
+ void PutCStringTruncated(const char *s, int right_pad) {
+ int bytes_left = GetWidth() - GetCursorX();
+ if (bytes_left > right_pad) {
+ bytes_left -= right_pad;
+ ::waddnstr(m_window, s, bytes_left);
+ }
+ }
+
+ void MoveWindow(const Point &origin) {
+ const bool moving_window = origin != GetParentOrigin();
+ if (m_is_subwin && moving_window) {
+ // Can't move subwindows, must delete and re-create
+ Size size = GetSize();
+ Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
+ origin.x),
+ true);
+ } else {
+ ::mvwin(m_window, origin.y, origin.x);
+ }
+ }
+
+ void SetBounds(const Rect &bounds) {
+ const bool moving_window = bounds.origin != GetParentOrigin();
+ if (m_is_subwin && moving_window) {
+ // Can't move subwindows, must delete and re-create
+ Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
+ bounds.origin.y, bounds.origin.x),
+ true);
+ } else {
+ if (moving_window)
+ MoveWindow(bounds.origin);
+ Resize(bounds.size);
+ }
+ }
+
+ void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
+ va_list args;
+ va_start(args, format);
+ vwprintw(m_window, format, args);
+ va_end(args);
+ }
+
+ void Touch() {
+ ::touchwin(m_window);
+ if (m_parent)
+ m_parent->Touch();
+ }
+
+ WindowSP CreateSubWindow(const char *name, const Rect &bounds,
+ bool make_active) {
+ auto get_window = [this, &bounds]() {
+ return m_window
+ ? ::subwin(m_window, bounds.size.height, bounds.size.width,
+ bounds.origin.y, bounds.origin.x)
+ : ::newwin(bounds.size.height, bounds.size.width,
+ bounds.origin.y, bounds.origin.x);
+ };
+ WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
+ subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
+ subwindow_sp->m_parent = this;
+ if (make_active) {
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ m_curr_active_window_idx = m_subwindows.size();
+ }
+ m_subwindows.push_back(subwindow_sp);
+ ::top_panel(subwindow_sp->m_panel);
+ m_needs_update = true;
+ return subwindow_sp;
+ }
+
+ bool RemoveSubWindow(Window *window) {
+ Windows::iterator pos, end = m_subwindows.end();
+ size_t i = 0;
+ for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
+ if ((*pos).get() == window) {
+ if (m_prev_active_window_idx == i)
+ m_prev_active_window_idx = UINT32_MAX;
+ else if (m_prev_active_window_idx != UINT32_MAX &&
+ m_prev_active_window_idx > i)
+ --m_prev_active_window_idx;
+
+ if (m_curr_active_window_idx == i)
+ m_curr_active_window_idx = UINT32_MAX;
+ else if (m_curr_active_window_idx != UINT32_MAX &&
+ m_curr_active_window_idx > i)
+ --m_curr_active_window_idx;
+ window->Erase();
+ m_subwindows.erase(pos);
+ m_needs_update = true;
+ if (m_parent)
+ m_parent->Touch();
+ else
+ ::touchwin(stdscr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ WindowSP FindSubWindow(const char *name) {
+ Windows::iterator pos, end = m_subwindows.end();
+ size_t i = 0;
+ for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
+ if ((*pos)->m_name == name)
+ return *pos;
+ }
+ return WindowSP();
+ }
+
+ void RemoveSubWindows() {
+ m_curr_active_window_idx = UINT32_MAX;
+ m_prev_active_window_idx = UINT32_MAX;
+ for (Windows::iterator pos = m_subwindows.begin();
+ pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
+ (*pos)->Erase();
+ }
+ if (m_parent)
+ m_parent->Touch();
+ else
+ ::touchwin(stdscr);
+ }
+
+ WINDOW *get() { return m_window; }
+
+ operator WINDOW *() { return m_window; }
+
+ // Window drawing utilities
+ void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
+ attr_t attr = 0;
+ if (IsActive())
+ attr = A_BOLD | COLOR_PAIR(2);
+ else
+ attr = 0;
+ if (attr)
+ AttributeOn(attr);
+
+ Box();
+ MoveCursor(3, 0);
+
+ if (title && title[0]) {
+ PutChar('<');
+ PutCString(title);
+ PutChar('>');
+ }
+
+ if (bottom_message && bottom_message[0]) {
+ int bottom_message_length = strlen(bottom_message);
+ int x = GetWidth() - 3 - (bottom_message_length + 2);
+
+ if (x > 0) {
+ MoveCursor(x, GetHeight() - 1);
+ PutChar('[');
+ PutCString(bottom_message);
+ PutChar(']');
+ } else {
+ MoveCursor(1, GetHeight() - 1);
+ PutChar('[');
+ PutCStringTruncated(bottom_message, 1);
+ }
+ }
+ if (attr)
+ AttributeOff(attr);
+ }
+
+ virtual void Draw(bool force) {
+ if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
+ return;
+
+ for (auto &subwindow_sp : m_subwindows)
+ subwindow_sp->Draw(force);
+ }
+
+ bool CreateHelpSubwindow() {
+ if (m_delegate_sp) {
+ const char *text = m_delegate_sp->WindowDelegateGetHelpText();
+ KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
+ if ((text && text[0]) || key_help) {
+ std::unique_ptr<HelpDialogDelegate> help_delegate_up(
+ new HelpDialogDelegate(text, key_help));
+ const size_t num_lines = help_delegate_up->GetNumLines();
+ const size_t max_length = help_delegate_up->GetMaxLineLength();
+ Rect bounds = GetBounds();
+ bounds.Inset(1, 1);
+ if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
+ bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
+ bounds.size.width = max_length + 4;
+ } else {
+ if (bounds.size.width > 100) {
+ const int inset_w = bounds.size.width / 4;
+ bounds.origin.x += inset_w;
+ bounds.size.width -= 2 * inset_w;
+ }
+ }
+
+ if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
+ bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
+ bounds.size.height = num_lines + 2;
+ } else {
+ if (bounds.size.height > 100) {
+ const int inset_h = bounds.size.height / 4;
+ bounds.origin.y += inset_h;
+ bounds.size.height -= 2 * inset_h;
+ }
+ }
+ WindowSP help_window_sp;
+ Window *parent_window = GetParent();
+ if (parent_window)
+ help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
+ else
+ help_window_sp = CreateSubWindow("Help", bounds, true);
+ help_window_sp->SetDelegate(
+ WindowDelegateSP(help_delegate_up.release()));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual HandleCharResult HandleChar(int key) {
+ // Always check the active window first
+ HandleCharResult result = eKeyNotHandled;
+ WindowSP active_window_sp = GetActiveWindow();
+ if (active_window_sp) {
+ result = active_window_sp->HandleChar(key);
+ if (result != eKeyNotHandled)
+ return result;
+ }
+
+ if (m_delegate_sp) {
+ result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
+ if (result != eKeyNotHandled)
+ return result;
+ }
+
+ // Then check for any windows that want any keys that weren't handled. This
+ // is typically only for a menubar. Make a copy of the subwindows in case
+ // any HandleChar() functions muck with the subwindows. If we don't do
+ // this, we can crash when iterating over the subwindows.
+ Windows subwindows(m_subwindows);
+ for (auto subwindow_sp : subwindows) {
+ if (!subwindow_sp->m_can_activate) {
+ HandleCharResult result = subwindow_sp->HandleChar(key);
+ if (result != eKeyNotHandled)
+ return result;
+ }
+ }
+
+ return eKeyNotHandled;
+ }
+
+ WindowSP GetActiveWindow() {
+ if (!m_subwindows.empty()) {
+ if (m_curr_active_window_idx >= m_subwindows.size()) {
+ if (m_prev_active_window_idx < m_subwindows.size()) {
+ m_curr_active_window_idx = m_prev_active_window_idx;
+ m_prev_active_window_idx = UINT32_MAX;
+ } else if (IsActive()) {
+ m_prev_active_window_idx = UINT32_MAX;
+ m_curr_active_window_idx = UINT32_MAX;
+
+ // Find first window that wants to be active if this window is active
+ const size_t num_subwindows = m_subwindows.size();
+ for (size_t i = 0; i < num_subwindows; ++i) {
+ if (m_subwindows[i]->GetCanBeActive()) {
+ m_curr_active_window_idx = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if (m_curr_active_window_idx < m_subwindows.size())
+ return m_subwindows[m_curr_active_window_idx];
+ }
+ return WindowSP();
+ }
+
+ bool GetCanBeActive() const { return m_can_activate; }
+
+ void SetCanBeActive(bool b) { m_can_activate = b; }
+
+ void SetDelegate(const WindowDelegateSP &delegate_sp) {
+ m_delegate_sp = delegate_sp;
+ }
+
+ Window *GetParent() const { return m_parent; }
+
+ bool IsActive() const {
+ if (m_parent)
+ return m_parent->GetActiveWindow().get() == this;
+ else
+ return true; // Top level window is always active
+ }
+
+ void SelectNextWindowAsActive() {
+ // Move active focus to next window
+ const size_t num_subwindows = m_subwindows.size();
+ if (m_curr_active_window_idx == UINT32_MAX) {
+ uint32_t idx = 0;
+ for (auto subwindow_sp : m_subwindows) {
+ if (subwindow_sp->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ break;
+ }
+ ++idx;
+ }
+ } else if (m_curr_active_window_idx + 1 < num_subwindows) {
+ bool handled = false;
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
+ ++idx) {
+ if (m_subwindows[idx]->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ handled = true;
+ break;
+ }
+ }
+ if (!handled) {
+ for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
+ if (m_subwindows[idx]->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ break;
+ }
+ }
+ }
+ } else {
+ m_prev_active_window_idx = m_curr_active_window_idx;
+ for (size_t idx = 0; idx < num_subwindows; ++idx) {
+ if (m_subwindows[idx]->GetCanBeActive()) {
+ m_curr_active_window_idx = idx;
+ break;
+ }
+ }
+ }
+ }
+
+ const char *GetName() const { return m_name.c_str(); }
+
+protected:
+ std::string m_name;
+ WINDOW *m_window;
+ PANEL *m_panel;
+ Window *m_parent;
+ Windows m_subwindows;
+ WindowDelegateSP m_delegate_sp;
+ uint32_t m_curr_active_window_idx;
+ uint32_t m_prev_active_window_idx;
+ bool m_delete;
+ bool m_needs_update;
+ bool m_can_activate;
+ bool m_is_subwin;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Window);
+};
+
+class MenuDelegate {
+public:
+ virtual ~MenuDelegate() = default;
+
+ virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
+};
+
+class Menu : public WindowDelegate {
+public:
+ enum class Type { Invalid, Bar, Item, Separator };
+
+ // Menubar or separator constructor
+ Menu(Type type);
+
+ // Menuitem constructor
+ Menu(const char *name, const char *key_name, int key_value,
+ uint64_t identifier);
+
+ ~Menu() override = default;
+
+ const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
+
+ void SetDelegate(const MenuDelegateSP &delegate_sp) {
+ m_delegate_sp = delegate_sp;
+ }
+
+ void RecalculateNameLengths();
+
+ void AddSubmenu(const MenuSP &menu_sp);
+
+ int DrawAndRunMenu(Window &window);
+
+ void DrawMenuTitle(Window &window, bool highlight);
+
+ bool WindowDelegateDraw(Window &window, bool force) override;
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
+
+ MenuActionResult ActionPrivate(Menu &menu) {
+ MenuActionResult result = MenuActionResult::NotHandled;
+ if (m_delegate_sp) {
+ result = m_delegate_sp->MenuDelegateAction(menu);
+ if (result != MenuActionResult::NotHandled)
+ return result;
+ } else if (m_parent) {
+ result = m_parent->ActionPrivate(menu);
+ if (result != MenuActionResult::NotHandled)
+ return result;
+ }
+ return m_canned_result;
+ }
+
+ MenuActionResult Action() {
+ // Call the recursive action so it can try to handle it with the menu
+ // delegate, and if not, try our parent menu
+ return ActionPrivate(*this);
+ }
+
+ void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
+
+ Menus &GetSubmenus() { return m_submenus; }
+
+ const Menus &GetSubmenus() const { return m_submenus; }
+
+ int GetSelectedSubmenuIndex() const { return m_selected; }
+
+ void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
+
+ Type GetType() const { return m_type; }
+
+ int GetStartingColumn() const { return m_start_col; }
+
+ void SetStartingColumn(int col) { m_start_col = col; }
+
+ int GetKeyValue() const { return m_key_value; }
+
+ std::string &GetName() { return m_name; }
+
+ int GetDrawWidth() const {
+ return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
+ }
+
+ uint64_t GetIdentifier() const { return m_identifier; }
+
+ void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
+
+protected:
+ std::string m_name;
+ std::string m_key_name;
+ uint64_t m_identifier;
+ Type m_type;
+ int m_key_value;
+ int m_start_col;
+ int m_max_submenu_name_length;
+ int m_max_submenu_key_name_length;
+ int m_selected;
+ Menu *m_parent;
+ Menus m_submenus;
+ WindowSP m_menu_window_sp;
+ MenuActionResult m_canned_result;
+ MenuDelegateSP m_delegate_sp;
+};
+
+// Menubar or separator constructor
+Menu::Menu(Type type)
+ : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
+ m_start_col(0), m_max_submenu_name_length(0),
+ m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
+ m_submenus(), m_canned_result(MenuActionResult::NotHandled),
+ m_delegate_sp() {}
+
+// Menuitem constructor
+Menu::Menu(const char *name, const char *key_name, int key_value,
+ uint64_t identifier)
+ : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
+ m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0),
+ m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
+ m_submenus(), m_canned_result(MenuActionResult::NotHandled),
+ m_delegate_sp() {
+ if (name && name[0]) {
+ m_name = name;
+ m_type = Type::Item;
+ if (key_name && key_name[0])
+ m_key_name = key_name;
+ } else {
+ m_type = Type::Separator;
+ }
+}
+
+void Menu::RecalculateNameLengths() {
+ m_max_submenu_name_length = 0;
+ m_max_submenu_key_name_length = 0;
+ Menus &submenus = GetSubmenus();
+ const size_t num_submenus = submenus.size();
+ for (size_t i = 0; i < num_submenus; ++i) {
+ Menu *submenu = submenus[i].get();
+ if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
+ m_max_submenu_name_length = submenu->m_name.size();
+ if (static_cast<size_t>(m_max_submenu_key_name_length) <
+ submenu->m_key_name.size())
+ m_max_submenu_key_name_length = submenu->m_key_name.size();
+ }
+}
+
+void Menu::AddSubmenu(const MenuSP &menu_sp) {
+ menu_sp->m_parent = this;
+ if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
+ m_max_submenu_name_length = menu_sp->m_name.size();
+ if (static_cast<size_t>(m_max_submenu_key_name_length) <
+ menu_sp->m_key_name.size())
+ m_max_submenu_key_name_length = menu_sp->m_key_name.size();
+ m_submenus.push_back(menu_sp);
+}
+
+void Menu::DrawMenuTitle(Window &window, bool highlight) {
+ if (m_type == Type::Separator) {
+ window.MoveCursor(0, window.GetCursorY());
+ window.PutChar(ACS_LTEE);
+ int width = window.GetWidth();
+ if (width > 2) {
+ width -= 2;
+ for (int i = 0; i < width; ++i)
+ window.PutChar(ACS_HLINE);
+ }
+ window.PutChar(ACS_RTEE);
+ } else {
+ const int shortcut_key = m_key_value;
+ bool underlined_shortcut = false;
+ const attr_t hilgight_attr = A_REVERSE;
+ if (highlight)
+ window.AttributeOn(hilgight_attr);
+ if (isprint(shortcut_key)) {
+ size_t lower_pos = m_name.find(tolower(shortcut_key));
+ size_t upper_pos = m_name.find(toupper(shortcut_key));
+ const char *name = m_name.c_str();
+ size_t pos = std::min<size_t>(lower_pos, upper_pos);
+ if (pos != std::string::npos) {
+ underlined_shortcut = true;
+ if (pos > 0) {
+ window.PutCString(name, pos);
+ name += pos;
+ }
+ const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
+ window.AttributeOn(shortcut_attr);
+ window.PutChar(name[0]);
+ window.AttributeOff(shortcut_attr);
+ name++;
+ if (name[0])
+ window.PutCString(name);
+ }
+ }
+
+ if (!underlined_shortcut) {
+ window.PutCString(m_name.c_str());
+ }
+
+ if (highlight)
+ window.AttributeOff(hilgight_attr);
+
+ if (m_key_name.empty()) {
+ if (!underlined_shortcut && isprint(m_key_value)) {
+ window.AttributeOn(COLOR_PAIR(3));
+ window.Printf(" (%c)", m_key_value);
+ window.AttributeOff(COLOR_PAIR(3));
+ }
+ } else {
+ window.AttributeOn(COLOR_PAIR(3));
+ window.Printf(" (%s)", m_key_name.c_str());
+ window.AttributeOff(COLOR_PAIR(3));
+ }
+ }
+}
+
+bool Menu::WindowDelegateDraw(Window &window, bool force) {
+ Menus &submenus = GetSubmenus();
+ const size_t num_submenus = submenus.size();
+ const int selected_idx = GetSelectedSubmenuIndex();
+ Menu::Type menu_type = GetType();
+ switch (menu_type) {
+ case Menu::Type::Bar: {
+ window.SetBackground(2);
+ window.MoveCursor(0, 0);
+ for (size_t i = 0; i < num_submenus; ++i) {
+ Menu *menu = submenus[i].get();
+ if (i > 0)
+ window.PutChar(' ');
+ menu->SetStartingColumn(window.GetCursorX());
+ window.PutCString("| ");
+ menu->DrawMenuTitle(window, false);
+ }
+ window.PutCString(" |");
+ } break;
+
+ case Menu::Type::Item: {
+ int y = 1;
+ int x = 3;
+ // Draw the menu
+ int cursor_x = 0;
+ int cursor_y = 0;
+ window.Erase();
+ window.SetBackground(2);
+ window.Box();
+ for (size_t i = 0; i < num_submenus; ++i) {
+ const bool is_selected = (i == static_cast<size_t>(selected_idx));
+ window.MoveCursor(x, y + i);
+ if (is_selected) {
+ // Remember where we want the cursor to be
+ cursor_x = x - 1;
+ cursor_y = y + i;
+ }
+ submenus[i]->DrawMenuTitle(window, is_selected);
+ }
+ window.MoveCursor(cursor_x, cursor_y);
+ } break;
+
+ default:
+ case Menu::Type::Separator:
+ break;
+ }
+ return true; // Drawing handled...
+}
+
+HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) {
+ HandleCharResult result = eKeyNotHandled;
+
+ Menus &submenus = GetSubmenus();
+ const size_t num_submenus = submenus.size();
+ const int selected_idx = GetSelectedSubmenuIndex();
+ Menu::Type menu_type = GetType();
+ if (menu_type == Menu::Type::Bar) {
+ MenuSP run_menu_sp;
+ switch (key) {
+ case KEY_DOWN:
+ case KEY_UP:
+ // Show last menu or first menu
+ if (selected_idx < static_cast<int>(num_submenus))
+ run_menu_sp = submenus[selected_idx];
+ else if (!submenus.empty())
+ run_menu_sp = submenus.front();
+ result = eKeyHandled;
+ break;
+
+ case KEY_RIGHT:
+ ++m_selected;
+ if (m_selected >= static_cast<int>(num_submenus))
+ m_selected = 0;
+ if (m_selected < static_cast<int>(num_submenus))
+ run_menu_sp = submenus[m_selected];
+ else if (!submenus.empty())
+ run_menu_sp = submenus.front();
+ result = eKeyHandled;
+ break;
+
+ case KEY_LEFT:
+ --m_selected;
+ if (m_selected < 0)
+ m_selected = num_submenus - 1;
+ if (m_selected < static_cast<int>(num_submenus))
+ run_menu_sp = submenus[m_selected];
+ else if (!submenus.empty())
+ run_menu_sp = submenus.front();
+ result = eKeyHandled;
+ break;
+
+ default:
+ for (size_t i = 0; i < num_submenus; ++i) {
+ if (submenus[i]->GetKeyValue() == key) {
+ SetSelectedSubmenuIndex(i);
+ run_menu_sp = submenus[i];
+ result = eKeyHandled;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (run_menu_sp) {
+ // Run the action on this menu in case we need to populate the menu with
+ // dynamic content and also in case check marks, and any other menu
+ // decorations need to be calculated
+ if (run_menu_sp->Action() == MenuActionResult::Quit)
+ return eQuitApplication;
+
+ Rect menu_bounds;
+ menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
+ menu_bounds.origin.y = 1;
+ menu_bounds.size.width = run_menu_sp->GetDrawWidth();
+ menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
+ if (m_menu_window_sp)
+ window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
+
+ m_menu_window_sp = window.GetParent()->CreateSubWindow(
+ run_menu_sp->GetName().c_str(), menu_bounds, true);
+ m_menu_window_sp->SetDelegate(run_menu_sp);
+ }
+ } else if (menu_type == Menu::Type::Item) {
+ switch (key) {
+ case KEY_DOWN:
+ if (m_submenus.size() > 1) {
+ const int start_select = m_selected;
+ while (++m_selected != start_select) {
+ if (static_cast<size_t>(m_selected) >= num_submenus)
+ m_selected = 0;
+ if (m_submenus[m_selected]->GetType() == Type::Separator)
+ continue;
+ else
+ break;
+ }
+ return eKeyHandled;
+ }
+ break;
+
+ case KEY_UP:
+ if (m_submenus.size() > 1) {
+ const int start_select = m_selected;
+ while (--m_selected != start_select) {
+ if (m_selected < static_cast<int>(0))
+ m_selected = num_submenus - 1;
+ if (m_submenus[m_selected]->GetType() == Type::Separator)
+ continue;
+ else
+ break;
+ }
+ return eKeyHandled;
+ }
+ break;
+
+ case KEY_RETURN:
+ if (static_cast<size_t>(selected_idx) < num_submenus) {
+ if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
+ return eQuitApplication;
+ window.GetParent()->RemoveSubWindow(&window);
+ return eKeyHandled;
+ }
+ break;
+
+ case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
+ // case other chars are entered for escaped sequences
+ window.GetParent()->RemoveSubWindow(&window);
+ return eKeyHandled;
+
+ default:
+ for (size_t i = 0; i < num_submenus; ++i) {
+ Menu *menu = submenus[i].get();
+ if (menu->GetKeyValue() == key) {
+ SetSelectedSubmenuIndex(i);
+ window.GetParent()->RemoveSubWindow(&window);
+ if (menu->Action() == MenuActionResult::Quit)
+ return eQuitApplication;
+ return eKeyHandled;
+ }
+ }
+ break;
+ }
+ } else if (menu_type == Menu::Type::Separator) {
+ }
+ return result;
+}
+
+class Application {
+public:
+ Application(FILE *in, FILE *out)
+ : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
+
+ ~Application() {
+ m_window_delegates.clear();
+ m_window_sp.reset();
+ if (m_screen) {
+ ::delscreen(m_screen);
+ m_screen = nullptr;
+ }
+ }
+
+ void Initialize() {
+ ::setlocale(LC_ALL, "");
+ ::setlocale(LC_CTYPE, "");
+ m_screen = ::newterm(nullptr, m_out, m_in);
+ ::start_color();
+ ::curs_set(0);
+ ::noecho();
+ ::keypad(stdscr, TRUE);
+ }
+
+ void Terminate() { ::endwin(); }
+
+ void Run(Debugger &debugger) {
+ bool done = false;
+ int delay_in_tenths_of_a_second = 1;
+
+ // Alas the threading model in curses is a bit lame so we need to resort to
+ // polling every 0.5 seconds. We could poll for stdin ourselves and then
+ // pass the keys down but then we need to translate all of the escape
+ // sequences ourselves. So we resort to polling for input because we need
+ // to receive async process events while in this loop.
+
+ halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
+ // of seconds seconds when calling
+ // Window::GetChar()
+
+ ListenerSP listener_sp(
+ Listener::MakeListener("lldb.IOHandler.curses.Application"));
+ ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
+ ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
+ debugger.EnableForwardEvents(listener_sp);
+
+ bool update = true;
+#if defined(__APPLE__)
+ std::deque<int> escape_chars;
+#endif
+
+ while (!done) {
+ if (update) {
+ m_window_sp->Draw(false);
+ // All windows should be calling Window::DeferredRefresh() instead of
+ // Window::Refresh() so we can do a single update and avoid any screen
+ // blinking
+ update_panels();
+
+ // Cursor hiding isn't working on MacOSX, so hide it in the top left
+ // corner
+ m_window_sp->MoveCursor(0, 0);
+
+ doupdate();
+ update = false;
+ }
+
+#if defined(__APPLE__)
+ // Terminal.app doesn't map its function keys correctly, F1-F4 default
+ // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
+ // possible
+ int ch;
+ if (escape_chars.empty())
+ ch = m_window_sp->GetChar();
+ else {
+ ch = escape_chars.front();
+ escape_chars.pop_front();
+ }
+ if (ch == KEY_ESCAPE) {
+ int ch2 = m_window_sp->GetChar();
+ if (ch2 == 'O') {
+ int ch3 = m_window_sp->GetChar();
+ switch (ch3) {
+ case 'P':
+ ch = KEY_F(1);
+ break;
+ case 'Q':
+ ch = KEY_F(2);
+ break;
+ case 'R':
+ ch = KEY_F(3);
+ break;
+ case 'S':
+ ch = KEY_F(4);
+ break;
+ default:
+ escape_chars.push_back(ch2);
+ if (ch3 != -1)
+ escape_chars.push_back(ch3);
+ break;
+ }
+ } else if (ch2 != -1)
+ escape_chars.push_back(ch2);
+ }
+#else
+ int ch = m_window_sp->GetChar();
+
+#endif
+ if (ch == -1) {
+ if (feof(m_in) || ferror(m_in)) {
+ done = true;
+ } else {
+ // Just a timeout from using halfdelay(), check for events
+ EventSP event_sp;
+ while (listener_sp->PeekAtNextEvent()) {
+ listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
+
+ if (event_sp) {
+ Broadcaster *broadcaster = event_sp->GetBroadcaster();
+ if (broadcaster) {
+ // uint32_t event_type = event_sp->GetType();
+ ConstString broadcaster_class(
+ broadcaster->GetBroadcasterClass());
+ if (broadcaster_class == broadcaster_class_process) {
+ debugger.GetCommandInterpreter().UpdateExecutionContext(
+ nullptr);
+ update = true;
+ continue; // Don't get any key, just update our view
+ }
+ }
+ }
+ }
+ }
+ } else {
+ HandleCharResult key_result = m_window_sp->HandleChar(ch);
+ switch (key_result) {
+ case eKeyHandled:
+ debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
+ update = true;
+ break;
+ case eKeyNotHandled:
+ break;
+ case eQuitApplication:
+ done = true;
+ break;
+ }
+ }
+ }
+
+ debugger.CancelForwardEvents(listener_sp);
+ }
+
+ WindowSP &GetMainWindow() {
+ if (!m_window_sp)
+ m_window_sp = std::make_shared<Window>("main", stdscr, false);
+ return m_window_sp;
+ }
+
+protected:
+ WindowSP m_window_sp;
+ WindowDelegates m_window_delegates;
+ SCREEN *m_screen;
+ FILE *m_in;
+ FILE *m_out;
+};
+
+} // namespace curses
+
+using namespace curses;
+
+struct Row {
+ ValueObjectManager value;
+ Row *parent;
+ // The process stop ID when the children were calculated.
+ uint32_t children_stop_id;
+ int row_idx;
+ int x;
+ int y;
+ bool might_have_children;
+ bool expanded;
+ bool calculated_children;
+ std::vector<Row> children;
+
+ Row(const ValueObjectSP &v, Row *p)
+ : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
+ x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
+ expanded(false), calculated_children(false), children() {}
+
+ size_t GetDepth() const {
+ if (parent)
+ return 1 + parent->GetDepth();
+ return 0;
+ }
+
+ void Expand() { expanded = true; }
+
+ std::vector<Row> &GetChildren() {
+ ProcessSP process_sp = value.GetProcessSP();
+ auto stop_id = process_sp->GetStopID();
+ if (process_sp && stop_id != children_stop_id) {
+ children_stop_id = stop_id;
+ calculated_children = false;
+ }
+ if (!calculated_children) {
+ children.clear();
+ calculated_children = true;
+ ValueObjectSP valobj = value.GetSP();
+ if (valobj) {
+ const size_t num_children = valobj->GetNumChildren();
+ for (size_t i = 0; i < num_children; ++i) {
+ children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
+ }
+ }
+ }
+ return children;
+ }
+
+ void Unexpand() {
+ expanded = false;
+ calculated_children = false;
+ children.clear();
+ }
+
+ void DrawTree(Window &window) {
+ if (parent)
+ parent->DrawTreeForChild(window, this, 0);
+
+ if (might_have_children) {
+ // It we can get UTF8 characters to work we should try to use the
+ // "symbol" UTF8 string below
+ // const char *symbol = "";
+ // if (row.expanded)
+ // symbol = "\xe2\x96\xbd ";
+ // else
+ // symbol = "\xe2\x96\xb7 ";
+ // window.PutCString (symbol);
+
+ // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
+ // or '>' character...
+ // if (expanded)
+ // window.PutChar (ACS_DARROW);
+ // else
+ // window.PutChar (ACS_RARROW);
+ // Since we can't find any good looking right arrow/down arrow symbols,
+ // just use a diamond...
+ window.PutChar(ACS_DIAMOND);
+ window.PutChar(ACS_HLINE);
+ }
+ }
+
+ void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
+ if (parent)
+ parent->DrawTreeForChild(window, this, reverse_depth + 1);
+
+ if (&GetChildren().back() == child) {
+ // Last child
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LLCORNER);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(' ');
+ window.PutChar(' ');
+ }
+ } else {
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LTEE);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(ACS_VLINE);
+ window.PutChar(' ');
+ }
+ }
+ }
+};
+
+struct DisplayOptions {
+ bool show_types;
+};
+
+class TreeItem;
+
+class TreeDelegate {
+public:
+ TreeDelegate() = default;
+ virtual ~TreeDelegate() = default;
+
+ virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
+ virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
+ virtual bool TreeDelegateItemSelected(
+ TreeItem &item) = 0; // Return true if we need to update views
+};
+
+typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
+
+class TreeItem {
+public:
+ TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
+ : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
+ m_identifier(0), m_row_idx(-1), m_children(),
+ m_might_have_children(might_have_children), m_is_expanded(false) {}
+
+ TreeItem &operator=(const TreeItem &rhs) {
+ if (this != &rhs) {
+ m_parent = rhs.m_parent;
+ m_delegate = rhs.m_delegate;
+ m_user_data = rhs.m_user_data;
+ m_identifier = rhs.m_identifier;
+ m_row_idx = rhs.m_row_idx;
+ m_children = rhs.m_children;
+ m_might_have_children = rhs.m_might_have_children;
+ m_is_expanded = rhs.m_is_expanded;
+ }
+ return *this;
+ }
+
+ size_t GetDepth() const {
+ if (m_parent)
+ return 1 + m_parent->GetDepth();
+ return 0;
+ }
+
+ int GetRowIndex() const { return m_row_idx; }
+
+ void ClearChildren() { m_children.clear(); }
+
+ void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
+
+ TreeItem &operator[](size_t i) { return m_children[i]; }
+
+ void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
+
+ size_t GetNumChildren() {
+ m_delegate.TreeDelegateGenerateChildren(*this);
+ return m_children.size();
+ }
+
+ void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
+
+ void CalculateRowIndexes(int &row_idx) {
+ SetRowIndex(row_idx);
+ ++row_idx;
+
+ const bool expanded = IsExpanded();
+
+ // The root item must calculate its children, or we must calculate the
+ // number of children if the item is expanded
+ if (m_parent == nullptr || expanded)
+ GetNumChildren();
+
+ for (auto &item : m_children) {
+ if (expanded)
+ item.CalculateRowIndexes(row_idx);
+ else
+ item.SetRowIndex(-1);
+ }
+ }
+
+ TreeItem *GetParent() { return m_parent; }
+
+ bool IsExpanded() const { return m_is_expanded; }
+
+ void Expand() { m_is_expanded = true; }
+
+ void Unexpand() { m_is_expanded = false; }
+
+ bool Draw(Window &window, const int first_visible_row,
+ const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
+ if (num_rows_left <= 0)
+ return false;
+
+ if (m_row_idx >= first_visible_row) {
+ window.MoveCursor(2, row_idx + 1);
+
+ if (m_parent)
+ m_parent->DrawTreeForChild(window, this, 0);
+
+ if (m_might_have_children) {
+ // It we can get UTF8 characters to work we should try to use the
+ // "symbol" UTF8 string below
+ // const char *symbol = "";
+ // if (row.expanded)
+ // symbol = "\xe2\x96\xbd ";
+ // else
+ // symbol = "\xe2\x96\xb7 ";
+ // window.PutCString (symbol);
+
+ // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
+ // 'v' or '>' character...
+ // if (expanded)
+ // window.PutChar (ACS_DARROW);
+ // else
+ // window.PutChar (ACS_RARROW);
+ // Since we can't find any good looking right arrow/down arrow symbols,
+ // just use a diamond...
+ window.PutChar(ACS_DIAMOND);
+ window.PutChar(ACS_HLINE);
+ }
+ bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
+ window.IsActive();
+
+ if (highlight)
+ window.AttributeOn(A_REVERSE);
+
+ m_delegate.TreeDelegateDrawTreeItem(*this, window);
+
+ if (highlight)
+ window.AttributeOff(A_REVERSE);
+ ++row_idx;
+ --num_rows_left;
+ }
+
+ if (num_rows_left <= 0)
+ return false; // We are done drawing...
+
+ if (IsExpanded()) {
+ for (auto &item : m_children) {
+ // If we displayed all the rows and item.Draw() returns false we are
+ // done drawing and can exit this for loop
+ if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
+ num_rows_left))
+ break;
+ }
+ }
+ return num_rows_left >= 0; // Return true if not done drawing yet
+ }
+
+ void DrawTreeForChild(Window &window, TreeItem *child,
+ uint32_t reverse_depth) {
+ if (m_parent)
+ m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
+
+ if (&m_children.back() == child) {
+ // Last child
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LLCORNER);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(' ');
+ window.PutChar(' ');
+ }
+ } else {
+ if (reverse_depth == 0) {
+ window.PutChar(ACS_LTEE);
+ window.PutChar(ACS_HLINE);
+ } else {
+ window.PutChar(ACS_VLINE);
+ window.PutChar(' ');
+ }
+ }
+ }
+
+ TreeItem *GetItemForRowIndex(uint32_t row_idx) {
+ if (static_cast<uint32_t>(m_row_idx) == row_idx)
+ return this;
+ if (m_children.empty())
+ return nullptr;
+ if (IsExpanded()) {
+ for (auto &item : m_children) {
+ TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
+ if (selected_item_ptr)
+ return selected_item_ptr;
+ }
+ }
+ return nullptr;
+ }
+
+ void *GetUserData() const { return m_user_data; }
+
+ void SetUserData(void *user_data) { m_user_data = user_data; }
+
+ uint64_t GetIdentifier() const { return m_identifier; }
+
+ void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
+
+ void SetMightHaveChildren(bool b) { m_might_have_children = b; }
+
+protected:
+ TreeItem *m_parent;
+ TreeDelegate &m_delegate;
+ void *m_user_data;
+ uint64_t m_identifier;
+ int m_row_idx; // Zero based visible row index, -1 if not visible or for the
+ // root item
+ std::vector<TreeItem> m_children;
+ bool m_might_have_children;
+ bool m_is_expanded;
+};
+
+class TreeWindowDelegate : public WindowDelegate {
+public:
+ TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
+ : m_debugger(debugger), m_delegate_sp(delegate_sp),
+ m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
+ m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
+ m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
+
+ int NumVisibleRows() const { return m_max_y - m_min_y; }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ Process *process = exe_ctx.GetProcessPtr();
+
+ bool display_content = false;
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsStoppedState(state, true)) {
+ // We are stopped, so it is ok to
+ display_content = true;
+ } else if (StateIsRunningState(state)) {
+ return true; // Don't do any updating when we are running
+ }
+ }
+
+ m_min_x = 2;
+ m_min_y = 1;
+ m_max_x = window.GetWidth() - 1;
+ m_max_y = window.GetHeight() - 1;
+
+ window.Erase();
+ window.DrawTitleBox(window.GetName());
+
+ if (display_content) {
+ const int num_visible_rows = NumVisibleRows();
+ m_num_rows = 0;
+ m_root.CalculateRowIndexes(m_num_rows);
+
+ // If we unexpanded while having something selected our total number of
+ // rows is less than the num visible rows, then make sure we show all the
+ // rows by setting the first visible row accordingly.
+ if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
+ m_first_visible_row = 0;
+
+ // Make sure the selected row is always visible
+ if (m_selected_row_idx < m_first_visible_row)
+ m_first_visible_row = m_selected_row_idx;
+ else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
+ m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
+
+ int row_idx = 0;
+ int num_rows_left = num_visible_rows;
+ m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
+ num_rows_left);
+ // Get the selected row
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ } else {
+ m_selected_item = nullptr;
+ }
+
+ return true; // Drawing handled
+ }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Thread window keyboard shortcuts:";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_UP, "Select previous item"},
+ {KEY_DOWN, "Select next item"},
+ {KEY_RIGHT, "Expand the selected item"},
+ {KEY_LEFT,
+ "Unexpand the selected item or select parent if not expanded"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'h', "Show help dialog"},
+ {' ', "Toggle item expansion"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
+ switch (c) {
+ case ',':
+ case KEY_PPAGE:
+ // Page up key
+ if (m_first_visible_row > 0) {
+ if (m_first_visible_row > m_max_y)
+ m_first_visible_row -= m_max_y;
+ else
+ m_first_visible_row = 0;
+ m_selected_row_idx = m_first_visible_row;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ return eKeyHandled;
+
+ case '.':
+ case KEY_NPAGE:
+ // Page down key
+ if (m_num_rows > m_max_y) {
+ if (m_first_visible_row + m_max_y < m_num_rows) {
+ m_first_visible_row += m_max_y;
+ m_selected_row_idx = m_first_visible_row;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ }
+ return eKeyHandled;
+
+ case KEY_UP:
+ if (m_selected_row_idx > 0) {
+ --m_selected_row_idx;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_selected_row_idx + 1 < m_num_rows) {
+ ++m_selected_row_idx;
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ return eKeyHandled;
+
+ case KEY_RIGHT:
+ if (m_selected_item) {
+ if (!m_selected_item->IsExpanded())
+ m_selected_item->Expand();
+ }
+ return eKeyHandled;
+
+ case KEY_LEFT:
+ if (m_selected_item) {
+ if (m_selected_item->IsExpanded())
+ m_selected_item->Unexpand();
+ else if (m_selected_item->GetParent()) {
+ m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
+ m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
+ if (m_selected_item)
+ m_selected_item->ItemWasSelected();
+ }
+ }
+ return eKeyHandled;
+
+ case ' ':
+ // Toggle expansion state when SPACE is pressed
+ if (m_selected_item) {
+ if (m_selected_item->IsExpanded())
+ m_selected_item->Unexpand();
+ else
+ m_selected_item->Expand();
+ }
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+protected:
+ Debugger &m_debugger;
+ TreeDelegateSP m_delegate_sp;
+ TreeItem m_root;
+ TreeItem *m_selected_item;
+ int m_num_rows;
+ int m_selected_row_idx;
+ int m_first_visible_row;
+ int m_min_x;
+ int m_min_y;
+ int m_max_x;
+ int m_max_y;
+};
+
+class FrameTreeDelegate : public TreeDelegate {
+public:
+ FrameTreeDelegate() : TreeDelegate() {
+ FormatEntity::Parse(
+ "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
+ m_format);
+ }
+
+ ~FrameTreeDelegate() override = default;
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ Thread *thread = (Thread *)item.GetUserData();
+ if (thread) {
+ const uint64_t frame_idx = item.GetIdentifier();
+ StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
+ if (frame_sp) {
+ StreamString strm;
+ const SymbolContext &sc =
+ frame_sp->GetSymbolContext(eSymbolContextEverything);
+ ExecutionContext exe_ctx(frame_sp);
+ if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
+ }
+ }
+ }
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ // No children for frames yet...
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override {
+ Thread *thread = (Thread *)item.GetUserData();
+ if (thread) {
+ thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
+ thread->GetID());
+ const uint64_t frame_idx = item.GetIdentifier();
+ thread->SetSelectedFrameByIndex(frame_idx);
+ return true;
+ }
+ return false;
+ }
+
+protected:
+ FormatEntity::Entry m_format;
+};
+
+class ThreadTreeDelegate : public TreeDelegate {
+public:
+ ThreadTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
+ m_stop_id(UINT32_MAX) {
+ FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
+ "reason = ${thread.stop-reason}}",
+ m_format);
+ }
+
+ ~ThreadTreeDelegate() override = default;
+
+ ProcessSP GetProcess() {
+ return m_debugger.GetCommandInterpreter()
+ .GetExecutionContext()
+ .GetProcessSP();
+ }
+
+ ThreadSP GetThread(const TreeItem &item) {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp)
+ return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
+ return ThreadSP();
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ ThreadSP thread_sp = GetThread(item);
+ if (thread_sp) {
+ StreamString strm;
+ ExecutionContext exe_ctx(thread_sp);
+ if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
+ }
+ }
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StateType state = process_sp->GetState();
+ if (StateIsStoppedState(state, true)) {
+ ThreadSP thread_sp = GetThread(item);
+ if (thread_sp) {
+ if (m_stop_id == process_sp->GetStopID() &&
+ thread_sp->GetID() == m_tid)
+ return; // Children are already up to date
+ if (!m_frame_delegate_sp) {
+ // Always expand the thread item the first time we show it
+ m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
+ }
+
+ m_stop_id = process_sp->GetStopID();
+ m_tid = thread_sp->GetID();
+
+ TreeItem t(&item, *m_frame_delegate_sp, false);
+ size_t num_frames = thread_sp->GetStackFrameCount();
+ item.Resize(num_frames, t);
+ for (size_t i = 0; i < num_frames; ++i) {
+ item[i].SetUserData(thread_sp.get());
+ item[i].SetIdentifier(i);
+ }
+ }
+ return;
+ }
+ }
+ item.ClearChildren();
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StateType state = process_sp->GetState();
+ if (StateIsStoppedState(state, true)) {
+ ThreadSP thread_sp = GetThread(item);
+ if (thread_sp) {
+ ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
+ ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
+ if (selected_thread_sp->GetID() != thread_sp->GetID()) {
+ thread_list.SetSelectedThreadByID(thread_sp->GetID());
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+protected:
+ Debugger &m_debugger;
+ std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
+ lldb::user_id_t m_tid;
+ uint32_t m_stop_id;
+ FormatEntity::Entry m_format;
+};
+
+class ThreadsTreeDelegate : public TreeDelegate {
+public:
+ ThreadsTreeDelegate(Debugger &debugger)
+ : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
+ m_stop_id(UINT32_MAX) {
+ FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
+ m_format);
+ }
+
+ ~ThreadsTreeDelegate() override = default;
+
+ ProcessSP GetProcess() {
+ return m_debugger.GetCommandInterpreter()
+ .GetExecutionContext()
+ .GetProcessSP();
+ }
+
+ void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StreamString strm;
+ ExecutionContext exe_ctx(process_sp);
+ if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
+ }
+ }
+ }
+
+ void TreeDelegateGenerateChildren(TreeItem &item) override {
+ ProcessSP process_sp = GetProcess();
+ if (process_sp && process_sp->IsAlive()) {
+ StateType state = process_sp->GetState();
+ if (StateIsStoppedState(state, true)) {
+ const uint32_t stop_id = process_sp->GetStopID();
+ if (m_stop_id == stop_id)
+ return; // Children are already up to date
+
+ m_stop_id = stop_id;
+
+ if (!m_thread_delegate_sp) {
+ // Always expand the thread item the first time we show it
+ // item.Expand();
+ m_thread_delegate_sp =
+ std::make_shared<ThreadTreeDelegate>(m_debugger);
+ }
+
+ TreeItem t(&item, *m_thread_delegate_sp, false);
+ ThreadList &threads = process_sp->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+ size_t num_threads = threads.GetSize();
+ item.Resize(num_threads, t);
+ for (size_t i = 0; i < num_threads; ++i) {
+ item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
+ item[i].SetMightHaveChildren(true);
+ }
+ return;
+ }
+ }
+ item.ClearChildren();
+ }
+
+ bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
+
+protected:
+ std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
+ Debugger &m_debugger;
+ uint32_t m_stop_id;
+ FormatEntity::Entry m_format;
+};
+
+class ValueObjectListDelegate : public WindowDelegate {
+public:
+ ValueObjectListDelegate()
+ : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
+ m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {}
+
+ ValueObjectListDelegate(ValueObjectList &valobj_list)
+ : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0),
+ m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {
+ SetValues(valobj_list);
+ }
+
+ ~ValueObjectListDelegate() override = default;
+
+ void SetValues(ValueObjectList &valobj_list) {
+ m_selected_row = nullptr;
+ m_selected_row_idx = 0;
+ m_first_visible_row = 0;
+ m_num_rows = 0;
+ m_rows.clear();
+ for (auto &valobj_sp : valobj_list.GetObjects())
+ m_rows.push_back(Row(valobj_sp, nullptr));
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ m_num_rows = 0;
+ m_min_x = 2;
+ m_min_y = 1;
+ m_max_x = window.GetWidth() - 1;
+ m_max_y = window.GetHeight() - 1;
+
+ window.Erase();
+ window.DrawTitleBox(window.GetName());
+
+ const int num_visible_rows = NumVisibleRows();
+ const int num_rows = CalculateTotalNumberRows(m_rows);
+
+ // If we unexpanded while having something selected our total number of
+ // rows is less than the num visible rows, then make sure we show all the
+ // rows by setting the first visible row accordingly.
+ if (m_first_visible_row > 0 && num_rows < num_visible_rows)
+ m_first_visible_row = 0;
+
+ // Make sure the selected row is always visible
+ if (m_selected_row_idx < m_first_visible_row)
+ m_first_visible_row = m_selected_row_idx;
+ else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
+ m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
+
+ DisplayRows(window, m_rows, g_options);
+
+ // Get the selected row
+ m_selected_row = GetRowForRowIndex(m_selected_row_idx);
+ // Keep the cursor on the selected row so the highlight and the cursor are
+ // always on the same line
+ if (m_selected_row)
+ window.MoveCursor(m_selected_row->x, m_selected_row->y);
+
+ return true; // Drawing handled
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_UP, "Select previous item"},
+ {KEY_DOWN, "Select next item"},
+ {KEY_RIGHT, "Expand selected item"},
+ {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'A', "Format as annotated address"},
+ {'b', "Format as binary"},
+ {'B', "Format as hex bytes with ASCII"},
+ {'c', "Format as character"},
+ {'d', "Format as a signed integer"},
+ {'D', "Format selected value using the default format for the type"},
+ {'f', "Format as float"},
+ {'h', "Show help dialog"},
+ {'i', "Format as instructions"},
+ {'o', "Format as octal"},
+ {'p', "Format as pointer"},
+ {'s', "Format as C string"},
+ {'t', "Toggle showing/hiding type names"},
+ {'u', "Format as an unsigned integer"},
+ {'x', "Format as hex"},
+ {'X', "Format as uppercase hex"},
+ {' ', "Toggle item expansion"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
+ switch (c) {
+ case 'x':
+ case 'X':
+ case 'o':
+ case 's':
+ case 'u':
+ case 'd':
+ case 'D':
+ case 'i':
+ case 'A':
+ case 'p':
+ case 'c':
+ case 'b':
+ case 'B':
+ case 'f':
+ // Change the format for the currently selected item
+ if (m_selected_row) {
+ auto valobj_sp = m_selected_row->value.GetSP();
+ if (valobj_sp)
+ valobj_sp->SetFormat(FormatForChar(c));
+ }
+ return eKeyHandled;
+
+ case 't':
+ // Toggle showing type names
+ g_options.show_types = !g_options.show_types;
+ return eKeyHandled;
+
+ case ',':
+ case KEY_PPAGE:
+ // Page up key
+ if (m_first_visible_row > 0) {
+ if (static_cast<int>(m_first_visible_row) > m_max_y)
+ m_first_visible_row -= m_max_y;
+ else
+ m_first_visible_row = 0;
+ m_selected_row_idx = m_first_visible_row;
+ }
+ return eKeyHandled;
+
+ case '.':
+ case KEY_NPAGE:
+ // Page down key
+ if (m_num_rows > static_cast<size_t>(m_max_y)) {
+ if (m_first_visible_row + m_max_y < m_num_rows) {
+ m_first_visible_row += m_max_y;
+ m_selected_row_idx = m_first_visible_row;
+ }
+ }
+ return eKeyHandled;
+
+ case KEY_UP:
+ if (m_selected_row_idx > 0)
+ --m_selected_row_idx;
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_selected_row_idx + 1 < m_num_rows)
+ ++m_selected_row_idx;
+ return eKeyHandled;
+
+ case KEY_RIGHT:
+ if (m_selected_row) {
+ if (!m_selected_row->expanded)
+ m_selected_row->Expand();
+ }
+ return eKeyHandled;
+
+ case KEY_LEFT:
+ if (m_selected_row) {
+ if (m_selected_row->expanded)
+ m_selected_row->Unexpand();
+ else if (m_selected_row->parent)
+ m_selected_row_idx = m_selected_row->parent->row_idx;
+ }
+ return eKeyHandled;
+
+ case ' ':
+ // Toggle expansion state when SPACE is pressed
+ if (m_selected_row) {
+ if (m_selected_row->expanded)
+ m_selected_row->Unexpand();
+ else
+ m_selected_row->Expand();
+ }
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+protected:
+ std::vector<Row> m_rows;
+ Row *m_selected_row;
+ uint32_t m_selected_row_idx;
+ uint32_t m_first_visible_row;
+ uint32_t m_num_rows;
+ int m_min_x;
+ int m_min_y;
+ int m_max_x;
+ int m_max_y;
+
+ static Format FormatForChar(int c) {
+ switch (c) {
+ case 'x':
+ return eFormatHex;
+ case 'X':
+ return eFormatHexUppercase;
+ case 'o':
+ return eFormatOctal;
+ case 's':
+ return eFormatCString;
+ case 'u':
+ return eFormatUnsigned;
+ case 'd':
+ return eFormatDecimal;
+ case 'D':
+ return eFormatDefault;
+ case 'i':
+ return eFormatInstruction;
+ case 'A':
+ return eFormatAddressInfo;
+ case 'p':
+ return eFormatPointer;
+ case 'c':
+ return eFormatChar;
+ case 'b':
+ return eFormatBinary;
+ case 'B':
+ return eFormatBytesWithASCII;
+ case 'f':
+ return eFormatFloat;
+ }
+ return eFormatDefault;
+ }
+
+ bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
+ bool highlight, bool last_child) {
+ ValueObject *valobj = row.value.GetSP().get();
+
+ if (valobj == nullptr)
+ return false;
+
+ const char *type_name =
+ options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
+ const char *name = valobj->GetName().GetCString();
+ const char *value = valobj->GetValueAsCString();
+ const char *summary = valobj->GetSummaryAsCString();
+
+ window.MoveCursor(row.x, row.y);
+
+ row.DrawTree(window);
+
+ if (highlight)
+ window.AttributeOn(A_REVERSE);
+
+ if (type_name && type_name[0])
+ window.Printf("(%s) ", type_name);
+
+ if (name && name[0])
+ window.PutCString(name);
+
+ attr_t changd_attr = 0;
+ if (valobj->GetValueDidChange())
+ changd_attr = COLOR_PAIR(5) | A_BOLD;
+
+ if (value && value[0]) {
+ window.PutCString(" = ");
+ if (changd_attr)
+ window.AttributeOn(changd_attr);
+ window.PutCString(value);
+ if (changd_attr)
+ window.AttributeOff(changd_attr);
+ }
+
+ if (summary && summary[0]) {
+ window.PutChar(' ');
+ if (changd_attr)
+ window.AttributeOn(changd_attr);
+ window.PutCString(summary);
+ if (changd_attr)
+ window.AttributeOff(changd_attr);
+ }
+
+ if (highlight)
+ window.AttributeOff(A_REVERSE);
+
+ return true;
+ }
+
+ void DisplayRows(Window &window, std::vector<Row> &rows,
+ DisplayOptions &options) {
+ // > 0x25B7
+ // \/ 0x25BD
+
+ bool window_is_active = window.IsActive();
+ for (auto &row : rows) {
+ const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
+ // Save the row index in each Row structure
+ row.row_idx = m_num_rows;
+ if ((m_num_rows >= m_first_visible_row) &&
+ ((m_num_rows - m_first_visible_row) <
+ static_cast<size_t>(NumVisibleRows()))) {
+ row.x = m_min_x;
+ row.y = m_num_rows - m_first_visible_row + 1;
+ if (DisplayRowObject(window, row, options,
+ window_is_active &&
+ m_num_rows == m_selected_row_idx,
+ last_child)) {
+ ++m_num_rows;
+ } else {
+ row.x = 0;
+ row.y = 0;
+ }
+ } else {
+ row.x = 0;
+ row.y = 0;
+ ++m_num_rows;
+ }
+
+ auto &children = row.GetChildren();
+ if (row.expanded && !children.empty()) {
+ DisplayRows(window, children, options);
+ }
+ }
+ }
+
+ int CalculateTotalNumberRows(std::vector<Row> &rows) {
+ int row_count = 0;
+ for (auto &row : rows) {
+ ++row_count;
+ if (row.expanded)
+ row_count += CalculateTotalNumberRows(row.GetChildren());
+ }
+ return row_count;
+ }
+
+ static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
+ for (auto &row : rows) {
+ if (row_index == 0)
+ return &row;
+ else {
+ --row_index;
+ auto &children = row.GetChildren();
+ if (row.expanded && !children.empty()) {
+ Row *result = GetRowForRowIndexImpl(children, row_index);
+ if (result)
+ return result;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ Row *GetRowForRowIndex(size_t row_index) {
+ return GetRowForRowIndexImpl(m_rows, row_index);
+ }
+
+ int NumVisibleRows() const { return m_max_y - m_min_y; }
+
+ static DisplayOptions g_options;
+};
+
+class FrameVariablesWindowDelegate : public ValueObjectListDelegate {
+public:
+ FrameVariablesWindowDelegate(Debugger &debugger)
+ : ValueObjectListDelegate(), m_debugger(debugger),
+ m_frame_block(nullptr) {}
+
+ ~FrameVariablesWindowDelegate() override = default;
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Frame variable window keyboard shortcuts:";
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ Process *process = exe_ctx.GetProcessPtr();
+ Block *frame_block = nullptr;
+ StackFrame *frame = nullptr;
+
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsStoppedState(state, true)) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame)
+ frame_block = frame->GetFrameBlock();
+ } else if (StateIsRunningState(state)) {
+ return true; // Don't do any updating when we are running
+ }
+ }
+
+ ValueObjectList local_values;
+ if (frame_block) {
+ // Only update the variables if they have changed
+ if (m_frame_block != frame_block) {
+ m_frame_block = frame_block;
+
+ VariableList *locals = frame->GetVariableList(true);
+ if (locals) {
+ const DynamicValueType use_dynamic = eDynamicDontRunTarget;
+ const size_t num_locals = locals->GetSize();
+ for (size_t i = 0; i < num_locals; ++i) {
+ ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable(
+ locals->GetVariableAtIndex(i), use_dynamic);
+ if (value_sp) {
+ ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
+ if (synthetic_value_sp)
+ local_values.Append(synthetic_value_sp);
+ else
+ local_values.Append(value_sp);
+ }
+ }
+ // Update the values
+ SetValues(local_values);
+ }
+ }
+ } else {
+ m_frame_block = nullptr;
+ // Update the values with an empty list if there is no frame
+ SetValues(local_values);
+ }
+
+ return ValueObjectListDelegate::WindowDelegateDraw(window, force);
+ }
+
+protected:
+ Debugger &m_debugger;
+ Block *m_frame_block;
+};
+
+class RegistersWindowDelegate : public ValueObjectListDelegate {
+public:
+ RegistersWindowDelegate(Debugger &debugger)
+ : ValueObjectListDelegate(), m_debugger(debugger) {}
+
+ ~RegistersWindowDelegate() override = default;
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Register window keyboard shortcuts:";
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx(
+ m_debugger.GetCommandInterpreter().GetExecutionContext());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+
+ ValueObjectList value_list;
+ if (frame) {
+ if (frame->GetStackID() != m_stack_id) {
+ m_stack_id = frame->GetStackID();
+ RegisterContextSP reg_ctx(frame->GetRegisterContext());
+ if (reg_ctx) {
+ const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
+ for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
+ value_list.Append(
+ ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
+ }
+ }
+ SetValues(value_list);
+ }
+ } else {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ return true; // Don't do any updating if we are running
+ else {
+ // Update the values with an empty list if there is no process or the
+ // process isn't alive anymore
+ SetValues(value_list);
+ }
+ }
+ return ValueObjectListDelegate::WindowDelegateDraw(window, force);
+ }
+
+protected:
+ Debugger &m_debugger;
+ StackID m_stack_id;
+};
+
+static const char *CursesKeyToCString(int ch) {
+ static char g_desc[32];
+ if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
+ snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
+ return g_desc;
+ }
+ switch (ch) {
+ case KEY_DOWN:
+ return "down";
+ case KEY_UP:
+ return "up";
+ case KEY_LEFT:
+ return "left";
+ case KEY_RIGHT:
+ return "right";
+ case KEY_HOME:
+ return "home";
+ case KEY_BACKSPACE:
+ return "backspace";
+ case KEY_DL:
+ return "delete-line";
+ case KEY_IL:
+ return "insert-line";
+ case KEY_DC:
+ return "delete-char";
+ case KEY_IC:
+ return "insert-char";
+ case KEY_CLEAR:
+ return "clear";
+ case KEY_EOS:
+ return "clear-to-eos";
+ case KEY_EOL:
+ return "clear-to-eol";
+ case KEY_SF:
+ return "scroll-forward";
+ case KEY_SR:
+ return "scroll-backward";
+ case KEY_NPAGE:
+ return "page-down";
+ case KEY_PPAGE:
+ return "page-up";
+ case KEY_STAB:
+ return "set-tab";
+ case KEY_CTAB:
+ return "clear-tab";
+ case KEY_CATAB:
+ return "clear-all-tabs";
+ case KEY_ENTER:
+ return "enter";
+ case KEY_PRINT:
+ return "print";
+ case KEY_LL:
+ return "lower-left key";
+ case KEY_A1:
+ return "upper left of keypad";
+ case KEY_A3:
+ return "upper right of keypad";
+ case KEY_B2:
+ return "center of keypad";
+ case KEY_C1:
+ return "lower left of keypad";
+ case KEY_C3:
+ return "lower right of keypad";
+ case KEY_BTAB:
+ return "back-tab key";
+ case KEY_BEG:
+ return "begin key";
+ case KEY_CANCEL:
+ return "cancel key";
+ case KEY_CLOSE:
+ return "close key";
+ case KEY_COMMAND:
+ return "command key";
+ case KEY_COPY:
+ return "copy key";
+ case KEY_CREATE:
+ return "create key";
+ case KEY_END:
+ return "end key";
+ case KEY_EXIT:
+ return "exit key";
+ case KEY_FIND:
+ return "find key";
+ case KEY_HELP:
+ return "help key";
+ case KEY_MARK:
+ return "mark key";
+ case KEY_MESSAGE:
+ return "message key";
+ case KEY_MOVE:
+ return "move key";
+ case KEY_NEXT:
+ return "next key";
+ case KEY_OPEN:
+ return "open key";
+ case KEY_OPTIONS:
+ return "options key";
+ case KEY_PREVIOUS:
+ return "previous key";
+ case KEY_REDO:
+ return "redo key";
+ case KEY_REFERENCE:
+ return "reference key";
+ case KEY_REFRESH:
+ return "refresh key";
+ case KEY_REPLACE:
+ return "replace key";
+ case KEY_RESTART:
+ return "restart key";
+ case KEY_RESUME:
+ return "resume key";
+ case KEY_SAVE:
+ return "save key";
+ case KEY_SBEG:
+ return "shifted begin key";
+ case KEY_SCANCEL:
+ return "shifted cancel key";
+ case KEY_SCOMMAND:
+ return "shifted command key";
+ case KEY_SCOPY:
+ return "shifted copy key";
+ case KEY_SCREATE:
+ return "shifted create key";
+ case KEY_SDC:
+ return "shifted delete-character key";
+ case KEY_SDL:
+ return "shifted delete-line key";
+ case KEY_SELECT:
+ return "select key";
+ case KEY_SEND:
+ return "shifted end key";
+ case KEY_SEOL:
+ return "shifted clear-to-end-of-line key";
+ case KEY_SEXIT:
+ return "shifted exit key";
+ case KEY_SFIND:
+ return "shifted find key";
+ case KEY_SHELP:
+ return "shifted help key";
+ case KEY_SHOME:
+ return "shifted home key";
+ case KEY_SIC:
+ return "shifted insert-character key";
+ case KEY_SLEFT:
+ return "shifted left-arrow key";
+ case KEY_SMESSAGE:
+ return "shifted message key";
+ case KEY_SMOVE:
+ return "shifted move key";
+ case KEY_SNEXT:
+ return "shifted next key";
+ case KEY_SOPTIONS:
+ return "shifted options key";
+ case KEY_SPREVIOUS:
+ return "shifted previous key";
+ case KEY_SPRINT:
+ return "shifted print key";
+ case KEY_SREDO:
+ return "shifted redo key";
+ case KEY_SREPLACE:
+ return "shifted replace key";
+ case KEY_SRIGHT:
+ return "shifted right-arrow key";
+ case KEY_SRSUME:
+ return "shifted resume key";
+ case KEY_SSAVE:
+ return "shifted save key";
+ case KEY_SSUSPEND:
+ return "shifted suspend key";
+ case KEY_SUNDO:
+ return "shifted undo key";
+ case KEY_SUSPEND:
+ return "suspend key";
+ case KEY_UNDO:
+ return "undo key";
+ case KEY_MOUSE:
+ return "Mouse event has occurred";
+ case KEY_RESIZE:
+ return "Terminal resize event";
+#ifdef KEY_EVENT
+ case KEY_EVENT:
+ return "We were interrupted by an event";
+#endif
+ case KEY_RETURN:
+ return "return";
+ case ' ':
+ return "space";
+ case '\t':
+ return "tab";
+ case KEY_ESCAPE:
+ return "escape";
+ default:
+ if (isprint(ch))
+ snprintf(g_desc, sizeof(g_desc), "%c", ch);
+ else
+ snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
+ return g_desc;
+ }
+ return nullptr;
+}
+
+HelpDialogDelegate::HelpDialogDelegate(const char *text,
+ KeyHelp *key_help_array)
+ : m_text(), m_first_visible_line(0) {
+ if (text && text[0]) {
+ m_text.SplitIntoLines(text);
+ m_text.AppendString("");
+ }
+ if (key_help_array) {
+ for (KeyHelp *key = key_help_array; key->ch; ++key) {
+ StreamString key_description;
+ key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
+ key->description);
+ m_text.AppendString(key_description.GetString());
+ }
+ }
+}
+
+HelpDialogDelegate::~HelpDialogDelegate() = default;
+
+bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
+ window.Erase();
+ const int window_height = window.GetHeight();
+ int x = 2;
+ int y = 1;
+ const int min_y = y;
+ const int max_y = window_height - 1 - y;
+ const size_t num_visible_lines = max_y - min_y + 1;
+ const size_t num_lines = m_text.GetSize();
+ const char *bottom_message;
+ if (num_lines <= num_visible_lines)
+ bottom_message = "Press any key to exit";
+ else
+ bottom_message = "Use arrows to scroll, any other key to exit";
+ window.DrawTitleBox(window.GetName(), bottom_message);
+ while (y <= max_y) {
+ window.MoveCursor(x, y);
+ window.PutCStringTruncated(
+ m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
+ ++y;
+ }
+ return true;
+}
+
+HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window,
+ int key) {
+ bool done = false;
+ const size_t num_lines = m_text.GetSize();
+ const size_t num_visible_lines = window.GetHeight() - 2;
+
+ if (num_lines <= num_visible_lines) {
+ done = true;
+ // If we have all lines visible and don't need scrolling, then any key
+ // press will cause us to exit
+ } else {
+ switch (key) {
+ case KEY_UP:
+ if (m_first_visible_line > 0)
+ --m_first_visible_line;
+ break;
+
+ case KEY_DOWN:
+ if (m_first_visible_line + num_visible_lines < num_lines)
+ ++m_first_visible_line;
+ break;
+
+ case KEY_PPAGE:
+ case ',':
+ if (m_first_visible_line > 0) {
+ if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
+ m_first_visible_line -= num_visible_lines;
+ else
+ m_first_visible_line = 0;
+ }
+ break;
+
+ case KEY_NPAGE:
+ case '.':
+ if (m_first_visible_line + num_visible_lines < num_lines) {
+ m_first_visible_line += num_visible_lines;
+ if (static_cast<size_t>(m_first_visible_line) > num_lines)
+ m_first_visible_line = num_lines - num_visible_lines;
+ }
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+ }
+ if (done)
+ window.GetParent()->RemoveSubWindow(&window);
+ return eKeyHandled;
+}
+
+class ApplicationDelegate : public WindowDelegate, public MenuDelegate {
+public:
+ enum {
+ eMenuID_LLDB = 1,
+ eMenuID_LLDBAbout,
+ eMenuID_LLDBExit,
+
+ eMenuID_Target,
+ eMenuID_TargetCreate,
+ eMenuID_TargetDelete,
+
+ eMenuID_Process,
+ eMenuID_ProcessAttach,
+ eMenuID_ProcessDetach,
+ eMenuID_ProcessLaunch,
+ eMenuID_ProcessContinue,
+ eMenuID_ProcessHalt,
+ eMenuID_ProcessKill,
+
+ eMenuID_Thread,
+ eMenuID_ThreadStepIn,
+ eMenuID_ThreadStepOver,
+ eMenuID_ThreadStepOut,
+
+ eMenuID_View,
+ eMenuID_ViewBacktrace,
+ eMenuID_ViewRegisters,
+ eMenuID_ViewSource,
+ eMenuID_ViewVariables,
+
+ eMenuID_Help,
+ eMenuID_HelpGUIHelp
+ };
+
+ ApplicationDelegate(Application &app, Debugger &debugger)
+ : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
+
+ ~ApplicationDelegate() override = default;
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ return false; // Drawing not handled, let standard window drawing happen
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
+ switch (key) {
+ case '\t':
+ window.SelectNextWindowAsActive();
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ case KEY_ESCAPE:
+ return eQuitApplication;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Welcome to the LLDB curses GUI.\n\n"
+ "Press the TAB key to change the selected view.\n"
+ "Each view has its own keyboard shortcuts, press 'h' to open a "
+ "dialog to display them.\n\n"
+ "Common key bindings for all views:";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {'\t', "Select next view"},
+ {'h', "Show help dialog with view specific key bindings"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {KEY_UP, "Select previous"},
+ {KEY_DOWN, "Select next"},
+ {KEY_LEFT, "Unexpand or select parent"},
+ {KEY_RIGHT, "Expand"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ MenuActionResult MenuDelegateAction(Menu &menu) override {
+ switch (menu.GetIdentifier()) {
+ case eMenuID_ThreadStepIn: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ exe_ctx.GetThreadRef().StepIn(true);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ThreadStepOut: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ exe_ctx.GetThreadRef().StepOut();
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ThreadStepOver: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ exe_ctx.GetThreadRef().StepOver(true);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessContinue: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true))
+ process->Resume();
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessKill: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ process->Destroy(false);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessHalt: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ process->Halt();
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ProcessDetach: {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope()) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive())
+ process->Detach(false);
+ }
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_Process: {
+ // Populate the menu with all of the threads if the process is stopped
+ // when the Process menu gets selected and is about to display its
+ // submenu.
+ Menus &submenus = menu.GetSubmenus();
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsAlive() &&
+ StateIsStoppedState(process->GetState(), true)) {
+ if (submenus.size() == 7)
+ menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
+ else if (submenus.size() > 8)
+ submenus.erase(submenus.begin() + 8, submenus.end());
+
+ ThreadList &threads = process->GetThreadList();
+ std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
+ size_t num_threads = threads.GetSize();
+ for (size_t i = 0; i < num_threads; ++i) {
+ ThreadSP thread_sp = threads.GetThreadAtIndex(i);
+ char menu_char = '\0';
+ if (i < 9)
+ menu_char = '1' + i;
+ StreamString thread_menu_title;
+ thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
+ const char *thread_name = thread_sp->GetName();
+ if (thread_name && thread_name[0])
+ thread_menu_title.Printf(" %s", thread_name);
+ else {
+ const char *queue_name = thread_sp->GetQueueName();
+ if (queue_name && queue_name[0])
+ thread_menu_title.Printf(" %s", queue_name);
+ }
+ menu.AddSubmenu(
+ MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
+ nullptr, menu_char, thread_sp->GetID())));
+ }
+ } else if (submenus.size() > 7) {
+ // Remove the separator and any other thread submenu items that were
+ // previously added
+ submenus.erase(submenus.begin() + 7, submenus.end());
+ }
+ // Since we are adding and removing items we need to recalculate the name
+ // lengths
+ menu.RecalculateNameLengths();
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ViewVariables: {
+ WindowSP main_window_sp = m_app.GetMainWindow();
+ WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
+ WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
+ WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
+ const Rect source_bounds = source_window_sp->GetBounds();
+
+ if (variables_window_sp) {
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+
+ main_window_sp->RemoveSubWindow(variables_window_sp.get());
+
+ if (registers_window_sp) {
+ // We have a registers window, so give all the area back to the
+ // registers window
+ Rect registers_bounds = variables_bounds;
+ registers_bounds.size.width = source_bounds.size.width;
+ registers_window_sp->SetBounds(registers_bounds);
+ } else {
+ // We have no registers window showing so give the bottom area back
+ // to the source view
+ source_window_sp->Resize(source_bounds.size.width,
+ source_bounds.size.height +
+ variables_bounds.size.height);
+ }
+ } else {
+ Rect new_variables_rect;
+ if (registers_window_sp) {
+ // We have a registers window so split the area of the registers
+ // window into two columns where the left hand side will be the
+ // variables and the right hand side will be the registers
+ const Rect variables_bounds = registers_window_sp->GetBounds();
+ Rect new_registers_rect;
+ variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
+ new_registers_rect);
+ registers_window_sp->SetBounds(new_registers_rect);
+ } else {
+ // No variables window, grab the bottom part of the source window
+ Rect new_source_rect;
+ source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
+ new_variables_rect);
+ source_window_sp->SetBounds(new_source_rect);
+ }
+ WindowSP new_window_sp = main_window_sp->CreateSubWindow(
+ "Variables", new_variables_rect, false);
+ new_window_sp->SetDelegate(
+ WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
+ }
+ touchwin(stdscr);
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_ViewRegisters: {
+ WindowSP main_window_sp = m_app.GetMainWindow();
+ WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
+ WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
+ WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
+ const Rect source_bounds = source_window_sp->GetBounds();
+
+ if (registers_window_sp) {
+ if (variables_window_sp) {
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+
+ // We have a variables window, so give all the area back to the
+ // variables window
+ variables_window_sp->Resize(variables_bounds.size.width +
+ registers_window_sp->GetWidth(),
+ variables_bounds.size.height);
+ } else {
+ // We have no variables window showing so give the bottom area back
+ // to the source view
+ source_window_sp->Resize(source_bounds.size.width,
+ source_bounds.size.height +
+ registers_window_sp->GetHeight());
+ }
+ main_window_sp->RemoveSubWindow(registers_window_sp.get());
+ } else {
+ Rect new_regs_rect;
+ if (variables_window_sp) {
+ // We have a variables window, split it into two columns where the
+ // left hand side will be the variables and the right hand side will
+ // be the registers
+ const Rect variables_bounds = variables_window_sp->GetBounds();
+ Rect new_vars_rect;
+ variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
+ new_regs_rect);
+ variables_window_sp->SetBounds(new_vars_rect);
+ } else {
+ // No registers window, grab the bottom part of the source window
+ Rect new_source_rect;
+ source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
+ new_regs_rect);
+ source_window_sp->SetBounds(new_source_rect);
+ }
+ WindowSP new_window_sp =
+ main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
+ new_window_sp->SetDelegate(
+ WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
+ }
+ touchwin(stdscr);
+ }
+ return MenuActionResult::Handled;
+
+ case eMenuID_HelpGUIHelp:
+ m_app.GetMainWindow()->CreateHelpSubwindow();
+ return MenuActionResult::Handled;
+
+ default:
+ break;
+ }
+
+ return MenuActionResult::NotHandled;
+ }
+
+protected:
+ Application &m_app;
+ Debugger &m_debugger;
+};
+
+class StatusBarWindowDelegate : public WindowDelegate {
+public:
+ StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
+ FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
+ }
+
+ ~StatusBarWindowDelegate() override = default;
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = exe_ctx.GetThreadPtr();
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ window.Erase();
+ window.SetBackground(2);
+ window.MoveCursor(0, 0);
+ if (process) {
+ const StateType state = process->GetState();
+ window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
+ StateAsCString(state));
+
+ if (StateIsStoppedState(state, true)) {
+ StreamString strm;
+ if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
+ nullptr, nullptr, false, false)) {
+ window.MoveCursor(40, 0);
+ window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
+ }
+
+ window.MoveCursor(60, 0);
+ if (frame)
+ window.Printf("Frame: %3u PC = 0x%16.16" PRIx64,
+ frame->GetFrameIndex(),
+ frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
+ exe_ctx.GetTargetPtr()));
+ } else if (state == eStateExited) {
+ const char *exit_desc = process->GetExitDescription();
+ const int exit_status = process->GetExitStatus();
+ if (exit_desc && exit_desc[0])
+ window.Printf(" with status = %i (%s)", exit_status, exit_desc);
+ else
+ window.Printf(" with status = %i", exit_status);
+ }
+ }
+ return true;
+ }
+
+protected:
+ Debugger &m_debugger;
+ FormatEntity::Entry m_format;
+};
+
+class SourceFileWindowDelegate : public WindowDelegate {
+public:
+ SourceFileWindowDelegate(Debugger &debugger)
+ : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
+ m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
+ m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
+ m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
+ m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
+
+ ~SourceFileWindowDelegate() override = default;
+
+ void Update(const SymbolContext &sc) { m_sc = sc; }
+
+ uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
+
+ const char *WindowDelegateGetHelpText() override {
+ return "Source/Disassembly window keyboard shortcuts:";
+ }
+
+ KeyHelp *WindowDelegateGetKeyHelp() override {
+ static curses::KeyHelp g_source_view_key_help[] = {
+ {KEY_RETURN, "Run to selected line with one shot breakpoint"},
+ {KEY_UP, "Select previous source line"},
+ {KEY_DOWN, "Select next source line"},
+ {KEY_PPAGE, "Page up"},
+ {KEY_NPAGE, "Page down"},
+ {'b', "Set breakpoint on selected source/disassembly line"},
+ {'c', "Continue process"},
+ {'d', "Detach and resume process"},
+ {'D', "Detach with process suspended"},
+ {'h', "Show help dialog"},
+ {'k', "Kill process"},
+ {'n', "Step over (source line)"},
+ {'N', "Step over (single instruction)"},
+ {'o', "Step out"},
+ {'s', "Step in (source line)"},
+ {'S', "Step in (single instruction)"},
+ {',', "Page up"},
+ {'.', "Page down"},
+ {'\0', nullptr}};
+ return g_source_view_key_help;
+ }
+
+ bool WindowDelegateDraw(Window &window, bool force) override {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ Process *process = exe_ctx.GetProcessPtr();
+ Thread *thread = nullptr;
+
+ bool update_location = false;
+ if (process) {
+ StateType state = process->GetState();
+ if (StateIsStoppedState(state, true)) {
+ // We are stopped, so it is ok to
+ update_location = true;
+ }
+ }
+
+ m_min_x = 1;
+ m_min_y = 2;
+ m_max_x = window.GetMaxX() - 1;
+ m_max_y = window.GetMaxY() - 1;
+
+ const uint32_t num_visible_lines = NumVisibleLines();
+ StackFrameSP frame_sp;
+ bool set_selected_line_to_pc = false;
+
+ if (update_location) {
+ const bool process_alive = process ? process->IsAlive() : false;
+ bool thread_changed = false;
+ if (process_alive) {
+ thread = exe_ctx.GetThreadPtr();
+ if (thread) {
+ frame_sp = thread->GetSelectedFrame();
+ auto tid = thread->GetID();
+ thread_changed = tid != m_tid;
+ m_tid = tid;
+ } else {
+ if (m_tid != LLDB_INVALID_THREAD_ID) {
+ thread_changed = true;
+ m_tid = LLDB_INVALID_THREAD_ID;
+ }
+ }
+ }
+ const uint32_t stop_id = process ? process->GetStopID() : 0;
+ const bool stop_id_changed = stop_id != m_stop_id;
+ bool frame_changed = false;
+ m_stop_id = stop_id;
+ m_title.Clear();
+ if (frame_sp) {
+ m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
+ if (m_sc.module_sp) {
+ m_title.Printf(
+ "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
+ ConstString func_name = m_sc.GetFunctionName();
+ if (func_name)
+ m_title.Printf("`%s", func_name.GetCString());
+ }
+ const uint32_t frame_idx = frame_sp->GetFrameIndex();
+ frame_changed = frame_idx != m_frame_idx;
+ m_frame_idx = frame_idx;
+ } else {
+ m_sc.Clear(true);
+ frame_changed = m_frame_idx != UINT32_MAX;
+ m_frame_idx = UINT32_MAX;
+ }
+
+ const bool context_changed =
+ thread_changed || frame_changed || stop_id_changed;
+
+ if (process_alive) {
+ if (m_sc.line_entry.IsValid()) {
+ m_pc_line = m_sc.line_entry.line;
+ if (m_pc_line != UINT32_MAX)
+ --m_pc_line; // Convert to zero based line number...
+ // Update the selected line if the stop ID changed...
+ if (context_changed)
+ m_selected_line = m_pc_line;
+
+ if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
+ // Same file, nothing to do, we should either have the lines or not
+ // (source file missing)
+ if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
+ if (m_selected_line >= m_first_visible_line + num_visible_lines)
+ m_first_visible_line = m_selected_line - 10;
+ } else {
+ if (m_selected_line > 10)
+ m_first_visible_line = m_selected_line - 10;
+ else
+ m_first_visible_line = 0;
+ }
+ } else {
+ // File changed, set selected line to the line with the PC
+ m_selected_line = m_pc_line;
+ m_file_sp =
+ m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
+ if (m_file_sp) {
+ const size_t num_lines = m_file_sp->GetNumLines();
+ m_line_width = 1;
+ for (size_t n = num_lines; n >= 10; n = n / 10)
+ ++m_line_width;
+
+ if (num_lines < num_visible_lines ||
+ m_selected_line < num_visible_lines)
+ m_first_visible_line = 0;
+ else
+ m_first_visible_line = m_selected_line - 10;
+ }
+ }
+ } else {
+ m_file_sp.reset();
+ }
+
+ if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
+ // Show disassembly
+ bool prefer_file_cache = false;
+ if (m_sc.function) {
+ if (m_disassembly_scope != m_sc.function) {
+ m_disassembly_scope = m_sc.function;
+ m_disassembly_sp = m_sc.function->GetInstructions(
+ exe_ctx, nullptr, prefer_file_cache);
+ if (m_disassembly_sp) {
+ set_selected_line_to_pc = true;
+ m_disassembly_range = m_sc.function->GetAddressRange();
+ } else {
+ m_disassembly_range.Clear();
+ }
+ } else {
+ set_selected_line_to_pc = context_changed;
+ }
+ } else if (m_sc.symbol) {
+ if (m_disassembly_scope != m_sc.symbol) {
+ m_disassembly_scope = m_sc.symbol;
+ m_disassembly_sp = m_sc.symbol->GetInstructions(
+ exe_ctx, nullptr, prefer_file_cache);
+ if (m_disassembly_sp) {
+ set_selected_line_to_pc = true;
+ m_disassembly_range.GetBaseAddress() =
+ m_sc.symbol->GetAddress();
+ m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
+ } else {
+ m_disassembly_range.Clear();
+ }
+ } else {
+ set_selected_line_to_pc = context_changed;
+ }
+ }
+ }
+ } else {
+ m_pc_line = UINT32_MAX;
+ }
+ }
+
+ const int window_width = window.GetWidth();
+ window.Erase();
+ window.DrawTitleBox("Sources");
+ if (!m_title.GetString().empty()) {
+ window.AttributeOn(A_REVERSE);
+ window.MoveCursor(1, 1);
+ window.PutChar(' ');
+ window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
+ int x = window.GetCursorX();
+ if (x < window_width - 1) {
+ window.Printf("%*s", window_width - x - 1, "");
+ }
+ window.AttributeOff(A_REVERSE);
+ }
+
+ Target *target = exe_ctx.GetTargetPtr();
+ const size_t num_source_lines = GetNumSourceLines();
+ if (num_source_lines > 0) {
+ // Display source
+ BreakpointLines bp_lines;
+ if (target) {
+ BreakpointList &bp_list = target->GetBreakpointList();
+ const size_t num_bps = bp_list.GetSize();
+ for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
+ BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
+ const size_t num_bps_locs = bp_sp->GetNumLocations();
+ for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
+ BreakpointLocationSP bp_loc_sp =
+ bp_sp->GetLocationAtIndex(bp_loc_idx);
+ LineEntry bp_loc_line_entry;
+ if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
+ bp_loc_line_entry)) {
+ if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
+ bp_lines.insert(bp_loc_line_entry.line);
+ }
+ }
+ }
+ }
+ }
+
+ const attr_t selected_highlight_attr = A_REVERSE;
+ const attr_t pc_highlight_attr = COLOR_PAIR(1);
+
+ for (size_t i = 0; i < num_visible_lines; ++i) {
+ const uint32_t curr_line = m_first_visible_line + i;
+ if (curr_line < num_source_lines) {
+ const int line_y = m_min_y + i;
+ window.MoveCursor(1, line_y);
+ const bool is_pc_line = curr_line == m_pc_line;
+ const bool line_is_selected = m_selected_line == curr_line;
+ // Highlight the line as the PC line first, then if the selected line
+ // isn't the same as the PC line, highlight it differently
+ attr_t highlight_attr = 0;
+ attr_t bp_attr = 0;
+ if (is_pc_line)
+ highlight_attr = pc_highlight_attr;
+ else if (line_is_selected)
+ highlight_attr = selected_highlight_attr;
+
+ if (bp_lines.find(curr_line + 1) != bp_lines.end())
+ bp_attr = COLOR_PAIR(2);
+
+ if (bp_attr)
+ window.AttributeOn(bp_attr);
+
+ window.Printf(" %*u ", m_line_width, curr_line + 1);
+
+ if (bp_attr)
+ window.AttributeOff(bp_attr);
+
+ window.PutChar(ACS_VLINE);
+ // Mark the line with the PC with a diamond
+ if (is_pc_line)
+ window.PutChar(ACS_DIAMOND);
+ else
+ window.PutChar(' ');
+
+ if (highlight_attr)
+ window.AttributeOn(highlight_attr);
+ const uint32_t line_len =
+ m_file_sp->GetLineLength(curr_line + 1, false);
+ if (line_len > 0)
+ window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
+
+ if (is_pc_line && frame_sp &&
+ frame_sp->GetConcreteFrameIndex() == 0) {
+ StopInfoSP stop_info_sp;
+ if (thread)
+ stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp) {
+ const char *stop_description = stop_info_sp->GetDescription();
+ if (stop_description && stop_description[0]) {
+ size_t stop_description_len = strlen(stop_description);
+ int desc_x = window_width - stop_description_len - 16;
+ window.Printf("%*s", desc_x - window.GetCursorX(), "");
+ // window.MoveCursor(window_width - stop_description_len - 15,
+ // line_y);
+ window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
+ stop_description);
+ }
+ } else {
+ window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
+ }
+ }
+ if (highlight_attr)
+ window.AttributeOff(highlight_attr);
+ } else {
+ break;
+ }
+ }
+ } else {
+ size_t num_disassembly_lines = GetNumDisassemblyLines();
+ if (num_disassembly_lines > 0) {
+ // Display disassembly
+ BreakpointAddrs bp_file_addrs;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ BreakpointList &bp_list = target->GetBreakpointList();
+ const size_t num_bps = bp_list.GetSize();
+ for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
+ BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
+ const size_t num_bps_locs = bp_sp->GetNumLocations();
+ for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
+ ++bp_loc_idx) {
+ BreakpointLocationSP bp_loc_sp =
+ bp_sp->GetLocationAtIndex(bp_loc_idx);
+ LineEntry bp_loc_line_entry;
+ const lldb::addr_t file_addr =
+ bp_loc_sp->GetAddress().GetFileAddress();
+ if (file_addr != LLDB_INVALID_ADDRESS) {
+ if (m_disassembly_range.ContainsFileAddress(file_addr))
+ bp_file_addrs.insert(file_addr);
+ }
+ }
+ }
+ }
+
+ const attr_t selected_highlight_attr = A_REVERSE;
+ const attr_t pc_highlight_attr = COLOR_PAIR(1);
+
+ StreamString strm;
+
+ InstructionList &insts = m_disassembly_sp->GetInstructionList();
+ Address pc_address;
+
+ if (frame_sp)
+ pc_address = frame_sp->GetFrameCodeAddress();
+ const uint32_t pc_idx =
+ pc_address.IsValid()
+ ? insts.GetIndexOfInstructionAtAddress(pc_address)
+ : UINT32_MAX;
+ if (set_selected_line_to_pc) {
+ m_selected_line = pc_idx;
+ }
+
+ const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
+ if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
+ m_first_visible_line = 0;
+
+ if (pc_idx < num_disassembly_lines) {
+ if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
+ pc_idx >= m_first_visible_line + num_visible_lines)
+ m_first_visible_line = pc_idx - non_visible_pc_offset;
+ }
+
+ for (size_t i = 0; i < num_visible_lines; ++i) {
+ const uint32_t inst_idx = m_first_visible_line + i;
+ Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
+ if (!inst)
+ break;
+
+ const int line_y = m_min_y + i;
+ window.MoveCursor(1, line_y);
+ const bool is_pc_line = frame_sp && inst_idx == pc_idx;
+ const bool line_is_selected = m_selected_line == inst_idx;
+ // Highlight the line as the PC line first, then if the selected line
+ // isn't the same as the PC line, highlight it differently
+ attr_t highlight_attr = 0;
+ attr_t bp_attr = 0;
+ if (is_pc_line)
+ highlight_attr = pc_highlight_attr;
+ else if (line_is_selected)
+ highlight_attr = selected_highlight_attr;
+
+ if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
+ bp_file_addrs.end())
+ bp_attr = COLOR_PAIR(2);
+
+ if (bp_attr)
+ window.AttributeOn(bp_attr);
+
+ window.Printf(" 0x%16.16llx ",
+ static_cast<unsigned long long>(
+ inst->GetAddress().GetLoadAddress(target)));
+
+ if (bp_attr)
+ window.AttributeOff(bp_attr);
+
+ window.PutChar(ACS_VLINE);
+ // Mark the line with the PC with a diamond
+ if (is_pc_line)
+ window.PutChar(ACS_DIAMOND);
+ else
+ window.PutChar(' ');
+
+ if (highlight_attr)
+ window.AttributeOn(highlight_attr);
+
+ const char *mnemonic = inst->GetMnemonic(&exe_ctx);
+ const char *operands = inst->GetOperands(&exe_ctx);
+ const char *comment = inst->GetComment(&exe_ctx);
+
+ if (mnemonic != nullptr && mnemonic[0] == '\0')
+ mnemonic = nullptr;
+ if (operands != nullptr && operands[0] == '\0')
+ operands = nullptr;
+ if (comment != nullptr && comment[0] == '\0')
+ comment = nullptr;
+
+ strm.Clear();
+
+ if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
+ strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
+ else if (mnemonic != nullptr && operands != nullptr)
+ strm.Printf("%-8s %s", mnemonic, operands);
+ else if (mnemonic != nullptr)
+ strm.Printf("%s", mnemonic);
+
+ int right_pad = 1;
+ window.PutCStringTruncated(strm.GetData(), right_pad);
+
+ if (is_pc_line && frame_sp &&
+ frame_sp->GetConcreteFrameIndex() == 0) {
+ StopInfoSP stop_info_sp;
+ if (thread)
+ stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp) {
+ const char *stop_description = stop_info_sp->GetDescription();
+ if (stop_description && stop_description[0]) {
+ size_t stop_description_len = strlen(stop_description);
+ int desc_x = window_width - stop_description_len - 16;
+ window.Printf("%*s", desc_x - window.GetCursorX(), "");
+ // window.MoveCursor(window_width - stop_description_len - 15,
+ // line_y);
+ window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
+ stop_description);
+ }
+ } else {
+ window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
+ }
+ }
+ if (highlight_attr)
+ window.AttributeOff(highlight_attr);
+ }
+ }
+ }
+ return true; // Drawing handled
+ }
+
+ size_t GetNumLines() {
+ size_t num_lines = GetNumSourceLines();
+ if (num_lines == 0)
+ num_lines = GetNumDisassemblyLines();
+ return num_lines;
+ }
+
+ size_t GetNumSourceLines() const {
+ if (m_file_sp)
+ return m_file_sp->GetNumLines();
+ return 0;
+ }
+
+ size_t GetNumDisassemblyLines() const {
+ if (m_disassembly_sp)
+ return m_disassembly_sp->GetInstructionList().GetSize();
+ return 0;
+ }
+
+ HandleCharResult WindowDelegateHandleChar(Window &window, int c) override {
+ const uint32_t num_visible_lines = NumVisibleLines();
+ const size_t num_lines = GetNumLines();
+
+ switch (c) {
+ case ',':
+ case KEY_PPAGE:
+ // Page up key
+ if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
+ m_first_visible_line -= num_visible_lines;
+ else
+ m_first_visible_line = 0;
+ m_selected_line = m_first_visible_line;
+ return eKeyHandled;
+
+ case '.':
+ case KEY_NPAGE:
+ // Page down key
+ {
+ if (m_first_visible_line + num_visible_lines < num_lines)
+ m_first_visible_line += num_visible_lines;
+ else if (num_lines < num_visible_lines)
+ m_first_visible_line = 0;
+ else
+ m_first_visible_line = num_lines - num_visible_lines;
+ m_selected_line = m_first_visible_line;
+ }
+ return eKeyHandled;
+
+ case KEY_UP:
+ if (m_selected_line > 0) {
+ m_selected_line--;
+ if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
+ m_first_visible_line = m_selected_line;
+ }
+ return eKeyHandled;
+
+ case KEY_DOWN:
+ if (m_selected_line + 1 < num_lines) {
+ m_selected_line++;
+ if (m_first_visible_line + num_visible_lines < m_selected_line)
+ m_first_visible_line++;
+ }
+ return eKeyHandled;
+
+ case '\r':
+ case '\n':
+ case KEY_ENTER:
+ // Set a breakpoint and run to the line using a one shot breakpoint
+ if (GetNumSourceLines() > 0) {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ nullptr, // Don't limit the breakpoint to certain modules
+ m_file_sp->GetFileSpec(), // Source file
+ m_selected_line +
+ 1, // Source line number (m_selected_line is zero based)
+ 0, // Unspecified column.
+ 0, // No offset
+ eLazyBoolCalculate, // Check inlines using global setting
+ eLazyBoolCalculate, // Skip prologue using global setting,
+ false, // internal
+ false, // request_hardware
+ eLazyBoolCalculate); // move_to_nearest_code
+ // Make breakpoint one shot
+ bp_sp->GetOptions()->SetOneShot(true);
+ exe_ctx.GetProcessRef().Resume();
+ }
+ } else if (m_selected_line < GetNumDisassemblyLines()) {
+ const Instruction *inst = m_disassembly_sp->GetInstructionList()
+ .GetInstructionAtIndex(m_selected_line)
+ .get();
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasTargetScope()) {
+ Address addr = inst->GetAddress();
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ addr, // lldb_private::Address
+ false, // internal
+ false); // request_hardware
+ // Make breakpoint one shot
+ bp_sp->GetOptions()->SetOneShot(true);
+ exe_ctx.GetProcessRef().Resume();
+ }
+ }
+ return eKeyHandled;
+
+ case 'b': // 'b' == toggle breakpoint on currently selected line
+ if (m_selected_line < GetNumSourceLines()) {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasTargetScope()) {
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ nullptr, // Don't limit the breakpoint to certain modules
+ m_file_sp->GetFileSpec(), // Source file
+ m_selected_line +
+ 1, // Source line number (m_selected_line is zero based)
+ 0, // No column specified.
+ 0, // No offset
+ eLazyBoolCalculate, // Check inlines using global setting
+ eLazyBoolCalculate, // Skip prologue using global setting,
+ false, // internal
+ false, // request_hardware
+ eLazyBoolCalculate); // move_to_nearest_code
+ }
+ } else if (m_selected_line < GetNumDisassemblyLines()) {
+ const Instruction *inst = m_disassembly_sp->GetInstructionList()
+ .GetInstructionAtIndex(m_selected_line)
+ .get();
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasTargetScope()) {
+ Address addr = inst->GetAddress();
+ BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
+ addr, // lldb_private::Address
+ false, // internal
+ false); // request_hardware
+ }
+ }
+ return eKeyHandled;
+
+ case 'd': // 'd' == detach and let run
+ case 'D': // 'D' == detach and keep stopped
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope())
+ exe_ctx.GetProcessRef().Detach(c == 'D');
+ }
+ return eKeyHandled;
+
+ case 'k':
+ // 'k' == kill
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope())
+ exe_ctx.GetProcessRef().Destroy(false);
+ }
+ return eKeyHandled;
+
+ case 'c':
+ // 'c' == continue
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasProcessScope())
+ exe_ctx.GetProcessRef().Resume();
+ }
+ return eKeyHandled;
+
+ case 'o':
+ // 'o' == step out
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope() &&
+ StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
+ exe_ctx.GetThreadRef().StepOut();
+ }
+ }
+ return eKeyHandled;
+
+ case 'n': // 'n' == step over
+ case 'N': // 'N' == step over instruction
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope() &&
+ StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
+ bool source_step = (c == 'n');
+ exe_ctx.GetThreadRef().StepOver(source_step);
+ }
+ }
+ return eKeyHandled;
+
+ case 's': // 's' == step into
+ case 'S': // 'S' == step into instruction
+ {
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+ if (exe_ctx.HasThreadScope() &&
+ StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
+ bool source_step = (c == 's');
+ exe_ctx.GetThreadRef().StepIn(source_step);
+ }
+ }
+ return eKeyHandled;
+
+ case 'h':
+ window.CreateHelpSubwindow();
+ return eKeyHandled;
+
+ default:
+ break;
+ }
+ return eKeyNotHandled;
+ }
+
+protected:
+ typedef std::set<uint32_t> BreakpointLines;
+ typedef std::set<lldb::addr_t> BreakpointAddrs;
+
+ Debugger &m_debugger;
+ SymbolContext m_sc;
+ SourceManager::FileSP m_file_sp;
+ SymbolContextScope *m_disassembly_scope;
+ lldb::DisassemblerSP m_disassembly_sp;
+ AddressRange m_disassembly_range;
+ StreamString m_title;
+ lldb::user_id_t m_tid;
+ int m_line_width;
+ uint32_t m_selected_line; // The selected line
+ uint32_t m_pc_line; // The line with the PC
+ uint32_t m_stop_id;
+ uint32_t m_frame_idx;
+ int m_first_visible_line;
+ int m_min_x;
+ int m_min_y;
+ int m_max_x;
+ int m_max_y;
+};
+
+DisplayOptions ValueObjectListDelegate::g_options = {true};
+
+IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
+ : IOHandler(debugger, IOHandler::Type::Curses) {}
+
+void IOHandlerCursesGUI::Activate() {
+ IOHandler::Activate();
+ if (!m_app_ap) {
+ m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE()));
+
+ // This is both a window and a menu delegate
+ std::shared_ptr<ApplicationDelegate> app_delegate_sp(
+ new ApplicationDelegate(*m_app_ap, m_debugger));
+
+ MenuDelegateSP app_menu_delegate_sp =
+ std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
+ MenuSP lldb_menu_sp(
+ new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
+ MenuSP exit_menuitem_sp(
+ new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
+ exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
+ lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
+ lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
+ lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
+
+ MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
+ ApplicationDelegate::eMenuID_Target));
+ target_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
+ target_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
+
+ MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
+ ApplicationDelegate::eMenuID_Process));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
+ process_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Continue", nullptr, 'c',
+ ApplicationDelegate::eMenuID_ProcessContinue)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
+ process_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
+
+ MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
+ ApplicationDelegate::eMenuID_Thread));
+ thread_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
+ thread_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Step Over", nullptr, 'v',
+ ApplicationDelegate::eMenuID_ThreadStepOver)));
+ thread_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
+
+ MenuSP view_menu_sp(
+ new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Backtrace", nullptr, 'b',
+ ApplicationDelegate::eMenuID_ViewBacktrace)));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Registers", nullptr, 'r',
+ ApplicationDelegate::eMenuID_ViewRegisters)));
+ view_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
+ view_menu_sp->AddSubmenu(
+ MenuSP(new Menu("Variables", nullptr, 'v',
+ ApplicationDelegate::eMenuID_ViewVariables)));
+
+ MenuSP help_menu_sp(
+ new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
+ help_menu_sp->AddSubmenu(MenuSP(new Menu(
+ "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
+
+ m_app_ap->Initialize();
+ WindowSP &main_window_sp = m_app_ap->GetMainWindow();
+
+ MenuSP menubar_sp(new Menu(Menu::Type::Bar));
+ menubar_sp->AddSubmenu(lldb_menu_sp);
+ menubar_sp->AddSubmenu(target_menu_sp);
+ menubar_sp->AddSubmenu(process_menu_sp);
+ menubar_sp->AddSubmenu(thread_menu_sp);
+ menubar_sp->AddSubmenu(view_menu_sp);
+ menubar_sp->AddSubmenu(help_menu_sp);
+ menubar_sp->SetDelegate(app_menu_delegate_sp);
+
+ Rect content_bounds = main_window_sp->GetFrame();
+ Rect menubar_bounds = content_bounds.MakeMenuBar();
+ Rect status_bounds = content_bounds.MakeStatusBar();
+ Rect source_bounds;
+ Rect variables_bounds;
+ Rect threads_bounds;
+ Rect source_variables_bounds;
+ content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
+ threads_bounds);
+ source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
+ variables_bounds);
+
+ WindowSP menubar_window_sp =
+ main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
+ // Let the menubar get keys if the active window doesn't handle the keys
+ // that are typed so it can respond to menubar key presses.
+ menubar_window_sp->SetCanBeActive(
+ false); // Don't let the menubar become the active window
+ menubar_window_sp->SetDelegate(menubar_sp);
+
+ WindowSP source_window_sp(
+ main_window_sp->CreateSubWindow("Source", source_bounds, true));
+ WindowSP variables_window_sp(
+ main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
+ WindowSP threads_window_sp(
+ main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
+ WindowSP status_window_sp(
+ main_window_sp->CreateSubWindow("Status", status_bounds, false));
+ status_window_sp->SetCanBeActive(
+ false); // Don't let the status bar become the active window
+ main_window_sp->SetDelegate(
+ std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
+ source_window_sp->SetDelegate(
+ WindowDelegateSP(new SourceFileWindowDelegate(m_debugger)));
+ variables_window_sp->SetDelegate(
+ WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger)));
+ TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
+ threads_window_sp->SetDelegate(WindowDelegateSP(
+ new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
+ status_window_sp->SetDelegate(
+ WindowDelegateSP(new StatusBarWindowDelegate(m_debugger)));
+
+ // Show the main help window once the first time the curses GUI is launched
+ static bool g_showed_help = false;
+ if (!g_showed_help) {
+ g_showed_help = true;
+ main_window_sp->CreateHelpSubwindow();
+ }
+
+ init_pair(1, COLOR_WHITE, COLOR_BLUE);
+ init_pair(2, COLOR_BLACK, COLOR_WHITE);
+ init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
+ init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(5, COLOR_RED, COLOR_BLACK);
+ }
+}
+
+void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); }
+
+void IOHandlerCursesGUI::Run() {
+ m_app_ap->Run(m_debugger);
+ SetIsDone(true);
+}
+
+IOHandlerCursesGUI::~IOHandlerCursesGUI() = default;
+
+void IOHandlerCursesGUI::Cancel() {}
+
+bool IOHandlerCursesGUI::Interrupt() { return false; }
+
+void IOHandlerCursesGUI::GotEOF() {}
+
+#endif // LLDB_DISABLE_CURSES
diff --git a/contrib/llvm-project/lldb/source/Core/Mangled.cpp b/contrib/llvm-project/lldb/source/Core/Mangled.cpp
new file mode 100644
index 000000000000..b06656aa3fb7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Mangled.cpp
@@ -0,0 +1,438 @@
+//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Mangled.h"
+
+#include "lldb/Core/RichManglingContext.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/Timer.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/Compiler.h"
+
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include <stdlib.h>
+#include <string.h>
+using namespace lldb_private;
+
+static inline Mangled::ManglingScheme cstring_mangling_scheme(const char *s) {
+ if (s) {
+ if (s[0] == '?')
+ return Mangled::eManglingSchemeMSVC;
+ if (s[0] == '_' && s[1] == 'Z')
+ return Mangled::eManglingSchemeItanium;
+ }
+ return Mangled::eManglingSchemeNone;
+}
+
+static inline bool cstring_is_mangled(const char *s) {
+ return cstring_mangling_scheme(s) != Mangled::eManglingSchemeNone;
+}
+
+static ConstString
+get_demangled_name_without_arguments(ConstString mangled,
+ ConstString demangled) {
+ // This pair is <mangled name, demangled name without function arguments>
+ static std::pair<ConstString, ConstString>
+ g_most_recent_mangled_to_name_sans_args;
+
+ // Need to have the mangled & demangled names we're currently examining as
+ // statics so we can return a const ref to them at the end of the func if we
+ // don't have anything better.
+ static ConstString g_last_mangled;
+ static ConstString g_last_demangled;
+
+ if (mangled && g_most_recent_mangled_to_name_sans_args.first == mangled) {
+ return g_most_recent_mangled_to_name_sans_args.second;
+ }
+
+ g_last_demangled = demangled;
+ g_last_mangled = mangled;
+
+ const char *mangled_name_cstr = mangled.GetCString();
+
+ if (demangled && mangled_name_cstr && mangled_name_cstr[0]) {
+ if (mangled_name_cstr[0] == '_' && mangled_name_cstr[1] == 'Z' &&
+ (mangled_name_cstr[2] != 'T' && // avoid virtual table, VTT structure,
+ // typeinfo structure, and typeinfo
+ // mangled_name
+ mangled_name_cstr[2] != 'G' && // avoid guard variables
+ mangled_name_cstr[2] != 'Z')) // named local entities (if we eventually
+ // handle eSymbolTypeData, we will want
+ // this back)
+ {
+ CPlusPlusLanguage::MethodName cxx_method(demangled);
+ if (!cxx_method.GetBasename().empty()) {
+ std::string shortname;
+ if (!cxx_method.GetContext().empty())
+ shortname = cxx_method.GetContext().str() + "::";
+ shortname += cxx_method.GetBasename().str();
+ ConstString result(shortname.c_str());
+ g_most_recent_mangled_to_name_sans_args.first = mangled;
+ g_most_recent_mangled_to_name_sans_args.second = result;
+ return g_most_recent_mangled_to_name_sans_args.second;
+ }
+ }
+ }
+
+ if (demangled)
+ return g_last_demangled;
+ return g_last_mangled;
+}
+
+#pragma mark Mangled
+
+Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() {
+ if (s)
+ SetValue(s);
+}
+
+Mangled::Mangled(llvm::StringRef name) {
+ if (!name.empty())
+ SetValue(ConstString(name));
+}
+
+// Convert to pointer operator. This allows code to check any Mangled objects
+// to see if they contain anything valid using code such as:
+//
+// Mangled mangled(...);
+// if (mangled)
+// { ...
+Mangled::operator void *() const {
+ return (m_mangled) ? const_cast<Mangled *>(this) : nullptr;
+}
+
+// Logical NOT operator. This allows code to check any Mangled objects to see
+// if they are invalid using code such as:
+//
+// Mangled mangled(...);
+// if (!file_spec)
+// { ...
+bool Mangled::operator!() const { return !m_mangled; }
+
+// Clear the mangled and demangled values.
+void Mangled::Clear() {
+ m_mangled.Clear();
+ m_demangled.Clear();
+}
+
+// Compare the string values.
+int Mangled::Compare(const Mangled &a, const Mangled &b) {
+ return ConstString::Compare(
+ a.GetName(lldb::eLanguageTypeUnknown, ePreferMangled),
+ b.GetName(lldb::eLanguageTypeUnknown, ePreferMangled));
+}
+
+// Set the string value in this objects. If "mangled" is true, then the mangled
+// named is set with the new value in "s", else the demangled name is set.
+void Mangled::SetValue(ConstString s, bool mangled) {
+ if (s) {
+ if (mangled) {
+ m_demangled.Clear();
+ m_mangled = s;
+ } else {
+ m_demangled = s;
+ m_mangled.Clear();
+ }
+ } else {
+ m_demangled.Clear();
+ m_mangled.Clear();
+ }
+}
+
+void Mangled::SetValue(ConstString name) {
+ if (name) {
+ if (cstring_is_mangled(name.GetCString())) {
+ m_demangled.Clear();
+ m_mangled = name;
+ } else {
+ m_demangled = name;
+ m_mangled.Clear();
+ }
+ } else {
+ m_demangled.Clear();
+ m_mangled.Clear();
+ }
+}
+
+// Local helpers for different demangling implementations.
+static char *GetMSVCDemangledStr(const char *M) {
+ char *demangled_cstr = llvm::microsoftDemangle(
+ M, nullptr, nullptr, nullptr,
+ llvm::MSDemangleFlags(llvm::MSDF_NoAccessSpecifier |
+ llvm::MSDF_NoCallingConvention |
+ llvm::MSDF_NoMemberType));
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
+ if (demangled_cstr && demangled_cstr[0])
+ LLDB_LOGF(log, "demangled msvc: %s -> \"%s\"", M, demangled_cstr);
+ else
+ LLDB_LOGF(log, "demangled msvc: %s -> error", M);
+ }
+
+ return demangled_cstr;
+}
+
+static char *GetItaniumDemangledStr(const char *M) {
+ char *demangled_cstr = nullptr;
+
+ llvm::ItaniumPartialDemangler ipd;
+ bool err = ipd.partialDemangle(M);
+ if (!err) {
+ // Default buffer and size (will realloc in case it's too small).
+ size_t demangled_size = 80;
+ demangled_cstr = static_cast<char *>(std::malloc(demangled_size));
+ demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size);
+
+ assert(demangled_cstr &&
+ "finishDemangle must always succeed if partialDemangle did");
+ assert(demangled_cstr[demangled_size - 1] == '\0' &&
+ "Expected demangled_size to return length including trailing null");
+ }
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
+ if (demangled_cstr)
+ LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr);
+ else
+ LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M);
+ }
+
+ return demangled_cstr;
+}
+
+// Explicit demangling for scheduled requests during batch processing. This
+// makes use of ItaniumPartialDemangler's rich demangle info
+bool Mangled::DemangleWithRichManglingInfo(
+ RichManglingContext &context, SkipMangledNameFn *skip_mangled_name) {
+ // We need to generate and cache the demangled name.
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Mangled::DemangleWithRichNameIndexInfo (m_mangled = %s)",
+ m_mangled.GetCString());
+
+ // Others are not meant to arrive here. ObjC names or C's main() for example
+ // have their names stored in m_demangled, while m_mangled is empty.
+ assert(m_mangled);
+
+ // Check whether or not we are interested in this name at all.
+ ManglingScheme scheme = cstring_mangling_scheme(m_mangled.GetCString());
+ if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme))
+ return false;
+
+ switch (scheme) {
+ case eManglingSchemeNone:
+ // The current mangled_name_filter would allow llvm_unreachable here.
+ return false;
+
+ case eManglingSchemeItanium:
+ // We want the rich mangling info here, so we don't care whether or not
+ // there is a demangled string in the pool already.
+ if (context.FromItaniumName(m_mangled)) {
+ // If we got an info, we have a name. Copy to string pool and connect the
+ // counterparts to accelerate later access in GetDemangledName().
+ context.ParseFullName();
+ m_demangled.SetStringWithMangledCounterpart(context.GetBufferRef(),
+ m_mangled);
+ return true;
+ } else {
+ m_demangled.SetCString("");
+ return false;
+ }
+
+ case eManglingSchemeMSVC: {
+ // We have no rich mangling for MSVC-mangled names yet, so first try to
+ // demangle it if necessary.
+ if (!m_demangled && !m_mangled.GetMangledCounterpart(m_demangled)) {
+ if (char *d = GetMSVCDemangledStr(m_mangled.GetCString())) {
+ // If we got an info, we have a name. Copy to string pool and connect
+ // the counterparts to accelerate later access in GetDemangledName().
+ m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(d),
+ m_mangled);
+ ::free(d);
+ } else {
+ m_demangled.SetCString("");
+ }
+ }
+
+ if (m_demangled.IsEmpty()) {
+ // Cannot demangle it, so don't try parsing.
+ return false;
+ } else {
+ // Demangled successfully, we can try and parse it with
+ // CPlusPlusLanguage::MethodName.
+ return context.FromCxxMethodName(m_demangled);
+ }
+ }
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+// Generate the demangled name on demand using this accessor. Code in this
+// class will need to use this accessor if it wishes to decode the demangled
+// name. The result is cached and will be kept until a new string value is
+// supplied to this object, or until the end of the object's lifetime.
+ConstString
+Mangled::GetDemangledName(lldb::LanguageType language) const {
+ // Check to make sure we have a valid mangled name and that we haven't
+ // already decoded our mangled name.
+ if (m_mangled && m_demangled.IsNull()) {
+ // We need to generate and cache the demangled name.
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Mangled::GetDemangledName (m_mangled = %s)",
+ m_mangled.GetCString());
+
+ // Don't bother running anything that isn't mangled
+ const char *mangled_name = m_mangled.GetCString();
+ ManglingScheme mangling_scheme{cstring_mangling_scheme(mangled_name)};
+ if (mangling_scheme != eManglingSchemeNone &&
+ !m_mangled.GetMangledCounterpart(m_demangled)) {
+ // We didn't already mangle this name, demangle it and if all goes well
+ // add it to our map.
+ char *demangled_name = nullptr;
+ switch (mangling_scheme) {
+ case eManglingSchemeMSVC:
+ demangled_name = GetMSVCDemangledStr(mangled_name);
+ break;
+ case eManglingSchemeItanium: {
+ demangled_name = GetItaniumDemangledStr(mangled_name);
+ break;
+ }
+ case eManglingSchemeNone:
+ llvm_unreachable("eManglingSchemeNone was handled already");
+ }
+ if (demangled_name) {
+ m_demangled.SetStringWithMangledCounterpart(
+ llvm::StringRef(demangled_name), m_mangled);
+ free(demangled_name);
+ }
+ }
+ if (m_demangled.IsNull()) {
+ // Set the demangled string to the empty string to indicate we tried to
+ // parse it once and failed.
+ m_demangled.SetCString("");
+ }
+ }
+
+ return m_demangled;
+}
+
+ConstString
+Mangled::GetDisplayDemangledName(lldb::LanguageType language) const {
+ return GetDemangledName(language);
+}
+
+bool Mangled::NameMatches(const RegularExpression &regex,
+ lldb::LanguageType language) const {
+ if (m_mangled && regex.Execute(m_mangled.AsCString()))
+ return true;
+
+ ConstString demangled = GetDemangledName(language);
+ return demangled && regex.Execute(demangled.AsCString());
+}
+
+// Get the demangled name if there is one, else return the mangled name.
+ConstString Mangled::GetName(lldb::LanguageType language,
+ Mangled::NamePreference preference) const {
+ if (preference == ePreferMangled && m_mangled)
+ return m_mangled;
+
+ ConstString demangled = GetDemangledName(language);
+
+ if (preference == ePreferDemangledWithoutArguments) {
+ return get_demangled_name_without_arguments(m_mangled, demangled);
+ }
+ if (preference == ePreferDemangled) {
+ // Call the accessor to make sure we get a demangled name in case it hasn't
+ // been demangled yet...
+ if (demangled)
+ return demangled;
+ return m_mangled;
+ }
+ return demangled;
+}
+
+// Dump a Mangled object to stream "s". We don't force our demangled name to be
+// computed currently (we don't use the accessor).
+void Mangled::Dump(Stream *s) const {
+ if (m_mangled) {
+ *s << ", mangled = " << m_mangled;
+ }
+ if (m_demangled) {
+ const char *demangled = m_demangled.AsCString();
+ s->Printf(", demangled = %s", demangled[0] ? demangled : "<error>");
+ }
+}
+
+// Dumps a debug version of this string with extra object and state information
+// to stream "s".
+void Mangled::DumpDebug(Stream *s) const {
+ s->Printf("%*p: Mangled mangled = ", static_cast<int>(sizeof(void *) * 2),
+ static_cast<const void *>(this));
+ m_mangled.DumpDebug(s);
+ s->Printf(", demangled = ");
+ m_demangled.DumpDebug(s);
+}
+
+// Return the size in byte that this object takes in memory. The size includes
+// the size of the objects it owns, and not the strings that it references
+// because they are shared strings.
+size_t Mangled::MemorySize() const {
+ return m_mangled.MemorySize() + m_demangled.MemorySize();
+}
+
+// We "guess" the language because we can't determine a symbol's language from
+// it's name. For example, a Pascal symbol can be mangled using the C++
+// Itanium scheme, and defined in a compilation unit within the same module as
+// other C++ units. In addition, different targets could have different ways
+// of mangling names from a given language, likewise the compilation units
+// within those targets.
+lldb::LanguageType Mangled::GuessLanguage() const {
+ ConstString mangled = GetMangledName();
+ if (mangled) {
+ const char *mangled_name = mangled.GetCString();
+ if (CPlusPlusLanguage::IsCPPMangledName(mangled_name))
+ return lldb::eLanguageTypeC_plus_plus;
+ else if (ObjCLanguage::IsPossibleObjCMethodName(mangled_name))
+ return lldb::eLanguageTypeObjC;
+ } else {
+ // ObjC names aren't really mangled, so they won't necessarily be in the
+ // mangled name slot.
+ ConstString demangled_name = GetDemangledName(lldb::eLanguageTypeUnknown);
+ if (demangled_name
+ && ObjCLanguage::IsPossibleObjCMethodName(demangled_name.GetCString()))
+ return lldb::eLanguageTypeObjC;
+
+ }
+ return lldb::eLanguageTypeUnknown;
+}
+
+// Dump OBJ to the supplied stream S.
+Stream &operator<<(Stream &s, const Mangled &obj) {
+ if (obj.GetMangledName())
+ s << "mangled = '" << obj.GetMangledName() << "'";
+
+ ConstString demangled =
+ obj.GetDemangledName(lldb::eLanguageTypeUnknown);
+ if (demangled)
+ s << ", demangled = '" << demangled << '\'';
+ else
+ s << ", demangled = <error>";
+ return s;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Module.cpp b/contrib/llvm-project/lldb/source/Core/Module.cpp
new file mode 100644
index 000000000000..aef3f3e3b4b0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Module.cpp
@@ -0,0 +1,1633 @@
+//===-- Module.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/AddressResolverFileLine.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Mangled.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/SymbolVendor.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/TypeMap.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBAssert.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/Timer.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+#include "Plugins/Language/ObjC/ObjCLanguage.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <assert.h>
+#include <cstdint>
+#include <inttypes.h>
+#include <map>
+#include <stdarg.h>
+#include <string.h>
+#include <type_traits>
+#include <utility>
+
+namespace lldb_private {
+class CompilerDeclContext;
+}
+namespace lldb_private {
+class VariableList;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Shared pointers to modules track module lifetimes in targets and in the
+// global module, but this collection will track all module objects that are
+// still alive
+typedef std::vector<Module *> ModuleCollection;
+
+static ModuleCollection &GetModuleCollection() {
+ // This module collection needs to live past any module, so we could either
+ // make it a shared pointer in each module or just leak is. Since it is only
+ // an empty vector by the time all the modules have gone away, we just leak
+ // it for now. If we decide this is a big problem we can introduce a
+ // Finalize method that will tear everything down in a predictable order.
+
+ static ModuleCollection *g_module_collection = nullptr;
+ if (g_module_collection == nullptr)
+ g_module_collection = new ModuleCollection();
+
+ return *g_module_collection;
+}
+
+std::recursive_mutex &Module::GetAllocationModuleCollectionMutex() {
+ // NOTE: The mutex below must be leaked since the global module list in
+ // the ModuleList class will get torn at some point, and we can't know if it
+ // will tear itself down before the "g_module_collection_mutex" below will.
+ // So we leak a Mutex object below to safeguard against that
+
+ static std::recursive_mutex *g_module_collection_mutex = nullptr;
+ if (g_module_collection_mutex == nullptr)
+ g_module_collection_mutex = new std::recursive_mutex; // NOTE: known leak
+ return *g_module_collection_mutex;
+}
+
+size_t Module::GetNumberAllocatedModules() {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ return GetModuleCollection().size();
+}
+
+Module *Module::GetAllocatedModuleAtIndex(size_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ ModuleCollection &modules = GetModuleCollection();
+ if (idx < modules.size())
+ return modules[idx];
+ return nullptr;
+}
+
+Module::Module(const ModuleSpec &module_spec)
+ : m_object_offset(0), m_file_has_changed(false),
+ m_first_file_changed_log(false) {
+ // Scope for locker below...
+ {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ GetModuleCollection().push_back(this);
+ }
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ LLDB_LOGF(log, "%p Module::Module((%s) '%s%s%s%s')",
+ static_cast<void *>(this),
+ module_spec.GetArchitecture().GetArchitectureName(),
+ module_spec.GetFileSpec().GetPath().c_str(),
+ module_spec.GetObjectName().IsEmpty() ? "" : "(",
+ module_spec.GetObjectName().IsEmpty()
+ ? ""
+ : module_spec.GetObjectName().AsCString(""),
+ module_spec.GetObjectName().IsEmpty() ? "" : ")");
+
+ // First extract all module specifications from the file using the local file
+ // path. If there are no specifications, then don't fill anything in
+ ModuleSpecList modules_specs;
+ if (ObjectFile::GetModuleSpecifications(module_spec.GetFileSpec(), 0, 0,
+ modules_specs) == 0)
+ return;
+
+ // Now make sure that one of the module specifications matches what we just
+ // extract. We might have a module specification that specifies a file
+ // "/usr/lib/dyld" with UUID XXX, but we might have a local version of
+ // "/usr/lib/dyld" that has
+ // UUID YYY and we don't want those to match. If they don't match, just don't
+ // fill any ivars in so we don't accidentally grab the wrong file later since
+ // they don't match...
+ ModuleSpec matching_module_spec;
+ if (!modules_specs.FindMatchingModuleSpec(module_spec,
+ matching_module_spec)) {
+ if (log) {
+ LLDB_LOGF(log, "Found local object file but the specs didn't match");
+ }
+ return;
+ }
+
+ if (module_spec.GetFileSpec())
+ m_mod_time = FileSystem::Instance().GetModificationTime(module_spec.GetFileSpec());
+ else if (matching_module_spec.GetFileSpec())
+ m_mod_time =
+ FileSystem::Instance().GetModificationTime(matching_module_spec.GetFileSpec());
+
+ // Copy the architecture from the actual spec if we got one back, else use
+ // the one that was specified
+ if (matching_module_spec.GetArchitecture().IsValid())
+ m_arch = matching_module_spec.GetArchitecture();
+ else if (module_spec.GetArchitecture().IsValid())
+ m_arch = module_spec.GetArchitecture();
+
+ // Copy the file spec over and use the specified one (if there was one) so we
+ // don't use a path that might have gotten resolved a path in
+ // 'matching_module_spec'
+ if (module_spec.GetFileSpec())
+ m_file = module_spec.GetFileSpec();
+ else if (matching_module_spec.GetFileSpec())
+ m_file = matching_module_spec.GetFileSpec();
+
+ // Copy the platform file spec over
+ if (module_spec.GetPlatformFileSpec())
+ m_platform_file = module_spec.GetPlatformFileSpec();
+ else if (matching_module_spec.GetPlatformFileSpec())
+ m_platform_file = matching_module_spec.GetPlatformFileSpec();
+
+ // Copy the symbol file spec over
+ if (module_spec.GetSymbolFileSpec())
+ m_symfile_spec = module_spec.GetSymbolFileSpec();
+ else if (matching_module_spec.GetSymbolFileSpec())
+ m_symfile_spec = matching_module_spec.GetSymbolFileSpec();
+
+ // Copy the object name over
+ if (matching_module_spec.GetObjectName())
+ m_object_name = matching_module_spec.GetObjectName();
+ else
+ m_object_name = module_spec.GetObjectName();
+
+ // Always trust the object offset (file offset) and object modification time
+ // (for mod time in a BSD static archive) of from the matching module
+ // specification
+ m_object_offset = matching_module_spec.GetObjectOffset();
+ m_object_mod_time = matching_module_spec.GetObjectModificationTime();
+}
+
+Module::Module(const FileSpec &file_spec, const ArchSpec &arch,
+ const ConstString *object_name, lldb::offset_t object_offset,
+ const llvm::sys::TimePoint<> &object_mod_time)
+ : m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_arch(arch),
+ m_file(file_spec), m_object_offset(object_offset),
+ m_object_mod_time(object_mod_time), m_file_has_changed(false),
+ m_first_file_changed_log(false) {
+ // Scope for locker below...
+ {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ GetModuleCollection().push_back(this);
+ }
+
+ if (object_name)
+ m_object_name = *object_name;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ LLDB_LOGF(log, "%p Module::Module((%s) '%s%s%s%s')",
+ static_cast<void *>(this), m_arch.GetArchitectureName(),
+ m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(",
+ m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""),
+ m_object_name.IsEmpty() ? "" : ")");
+}
+
+Module::Module()
+ : m_object_offset(0), m_file_has_changed(false),
+ m_first_file_changed_log(false) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ GetModuleCollection().push_back(this);
+}
+
+Module::~Module() {
+ // Lock our module down while we tear everything down to make sure we don't
+ // get any access to the module while it is being destroyed
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // Scope for locker below...
+ {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetAllocationModuleCollectionMutex());
+ ModuleCollection &modules = GetModuleCollection();
+ ModuleCollection::iterator end = modules.end();
+ ModuleCollection::iterator pos = std::find(modules.begin(), end, this);
+ assert(pos != end);
+ modules.erase(pos);
+ }
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT |
+ LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ LLDB_LOGF(log, "%p Module::~Module((%s) '%s%s%s%s')",
+ static_cast<void *>(this), m_arch.GetArchitectureName(),
+ m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(",
+ m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""),
+ m_object_name.IsEmpty() ? "" : ")");
+ // Release any auto pointers before we start tearing down our member
+ // variables since the object file and symbol files might need to make
+ // function calls back into this module object. The ordering is important
+ // here because symbol files can require the module object file. So we tear
+ // down the symbol file first, then the object file.
+ m_sections_up.reset();
+ m_symfile_up.reset();
+ m_objfile_sp.reset();
+}
+
+ObjectFile *Module::GetMemoryObjectFile(const lldb::ProcessSP &process_sp,
+ lldb::addr_t header_addr, Status &error,
+ size_t size_to_read) {
+ if (m_objfile_sp) {
+ error.SetErrorString("object file already exists");
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (process_sp) {
+ m_did_load_objfile = true;
+ auto data_up = std::make_unique<DataBufferHeap>(size_to_read, 0);
+ Status readmem_error;
+ const size_t bytes_read =
+ process_sp->ReadMemory(header_addr, data_up->GetBytes(),
+ data_up->GetByteSize(), readmem_error);
+ if (bytes_read == size_to_read) {
+ DataBufferSP data_sp(data_up.release());
+ m_objfile_sp = ObjectFile::FindPlugin(shared_from_this(), process_sp,
+ header_addr, data_sp);
+ if (m_objfile_sp) {
+ StreamString s;
+ s.Printf("0x%16.16" PRIx64, header_addr);
+ m_object_name.SetString(s.GetString());
+
+ // Once we get the object file, update our module with the object
+ // file's architecture since it might differ in vendor/os if some
+ // parts were unknown.
+ m_arch = m_objfile_sp->GetArchitecture();
+
+ // Augment the arch with the target's information in case
+ // we are unable to extract the os/environment from memory.
+ m_arch.MergeFrom(process_sp->GetTarget().GetArchitecture());
+ } else {
+ error.SetErrorString("unable to find suitable object file plug-in");
+ }
+ } else {
+ error.SetErrorStringWithFormat("unable to read header from memory: %s",
+ readmem_error.AsCString());
+ }
+ } else {
+ error.SetErrorString("invalid process");
+ }
+ }
+ return m_objfile_sp.get();
+}
+
+const lldb_private::UUID &Module::GetUUID() {
+ if (!m_did_set_uuid.load()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_set_uuid.load()) {
+ ObjectFile *obj_file = GetObjectFile();
+
+ if (obj_file != nullptr) {
+ m_uuid = obj_file->GetUUID();
+ m_did_set_uuid = true;
+ }
+ }
+ }
+ return m_uuid;
+}
+
+void Module::SetUUID(const lldb_private::UUID &uuid) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_set_uuid) {
+ m_uuid = uuid;
+ m_did_set_uuid = true;
+ } else {
+ lldbassert(0 && "Attempting to overwrite the existing module UUID");
+ }
+}
+
+llvm::Expected<TypeSystem &>
+Module::GetTypeSystemForLanguage(LanguageType language) {
+ return m_type_system_map.GetTypeSystemForLanguage(language, this, true);
+}
+
+void Module::ParseAllDebugSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t num_comp_units = GetNumCompileUnits();
+ if (num_comp_units == 0)
+ return;
+
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ SymbolFile *symbols = GetSymbolFile();
+
+ for (size_t cu_idx = 0; cu_idx < num_comp_units; cu_idx++) {
+ sc.comp_unit = symbols->GetCompileUnitAtIndex(cu_idx).get();
+ if (!sc.comp_unit)
+ continue;
+
+ symbols->ParseVariablesForContext(sc);
+
+ symbols->ParseFunctions(*sc.comp_unit);
+
+ sc.comp_unit->ForeachFunction([&sc, &symbols](const FunctionSP &f) {
+ symbols->ParseBlocksRecursive(*f);
+
+ // Parse the variables for this function and all its blocks
+ sc.function = f.get();
+ symbols->ParseVariablesForContext(sc);
+ return false;
+ });
+
+ // Parse all types for this compile unit
+ symbols->ParseTypes(*sc.comp_unit);
+ }
+}
+
+void Module::CalculateSymbolContext(SymbolContext *sc) {
+ sc->module_sp = shared_from_this();
+}
+
+ModuleSP Module::CalculateSymbolContextModule() { return shared_from_this(); }
+
+void Module::DumpSymbolContext(Stream *s) {
+ s->Printf(", Module{%p}", static_cast<void *>(this));
+}
+
+size_t Module::GetNumCompileUnits() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Module::GetNumCompileUnits (module = %p)",
+ static_cast<void *>(this));
+ if (SymbolFile *symbols = GetSymbolFile())
+ return symbols->GetNumCompileUnits();
+ return 0;
+}
+
+CompUnitSP Module::GetCompileUnitAtIndex(size_t index) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t num_comp_units = GetNumCompileUnits();
+ CompUnitSP cu_sp;
+
+ if (index < num_comp_units) {
+ if (SymbolFile *symbols = GetSymbolFile())
+ cu_sp = symbols->GetCompileUnitAtIndex(index);
+ }
+ return cu_sp;
+}
+
+bool Module::ResolveFileAddress(lldb::addr_t vm_addr, Address &so_addr) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Module::ResolveFileAddress (vm_addr = 0x%" PRIx64 ")",
+ vm_addr);
+ SectionList *section_list = GetSectionList();
+ if (section_list)
+ return so_addr.ResolveAddressUsingFileSections(vm_addr, section_list);
+ return false;
+}
+
+uint32_t Module::ResolveSymbolContextForAddress(
+ const Address &so_addr, lldb::SymbolContextItem resolve_scope,
+ SymbolContext &sc, bool resolve_tail_call_address) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ uint32_t resolved_flags = 0;
+
+ // Clear the result symbol context in case we don't find anything, but don't
+ // clear the target
+ sc.Clear(false);
+
+ // Get the section from the section/offset address.
+ SectionSP section_sp(so_addr.GetSection());
+
+ // Make sure the section matches this module before we try and match anything
+ if (section_sp && section_sp->GetModule().get() == this) {
+ // If the section offset based address resolved itself, then this is the
+ // right module.
+ sc.module_sp = shared_from_this();
+ resolved_flags |= eSymbolContextModule;
+
+ SymbolFile *symfile = GetSymbolFile();
+ if (!symfile)
+ return resolved_flags;
+
+ // Resolve the compile unit, function, block, line table or line entry if
+ // requested.
+ if (resolve_scope & eSymbolContextCompUnit ||
+ resolve_scope & eSymbolContextFunction ||
+ resolve_scope & eSymbolContextBlock ||
+ resolve_scope & eSymbolContextLineEntry ||
+ resolve_scope & eSymbolContextVariable) {
+ resolved_flags |=
+ symfile->ResolveSymbolContext(so_addr, resolve_scope, sc);
+ }
+
+ // Resolve the symbol if requested, but don't re-look it up if we've
+ // already found it.
+ if (resolve_scope & eSymbolContextSymbol &&
+ !(resolved_flags & eSymbolContextSymbol)) {
+ Symtab *symtab = symfile->GetSymtab();
+ if (symtab && so_addr.IsSectionOffset()) {
+ Symbol *matching_symbol = nullptr;
+
+ symtab->ForEachSymbolContainingFileAddress(
+ so_addr.GetFileAddress(),
+ [&matching_symbol](Symbol *symbol) -> bool {
+ if (symbol->GetType() != eSymbolTypeInvalid) {
+ matching_symbol = symbol;
+ return false; // Stop iterating
+ }
+ return true; // Keep iterating
+ });
+ sc.symbol = matching_symbol;
+ if (!sc.symbol && resolve_scope & eSymbolContextFunction &&
+ !(resolved_flags & eSymbolContextFunction)) {
+ bool verify_unique = false; // No need to check again since
+ // ResolveSymbolContext failed to find a
+ // symbol at this address.
+ if (ObjectFile *obj_file = sc.module_sp->GetObjectFile())
+ sc.symbol =
+ obj_file->ResolveSymbolForAddress(so_addr, verify_unique);
+ }
+
+ if (sc.symbol) {
+ if (sc.symbol->IsSynthetic()) {
+ // We have a synthetic symbol so lets check if the object file from
+ // the symbol file in the symbol vendor is different than the
+ // object file for the module, and if so search its symbol table to
+ // see if we can come up with a better symbol. For example dSYM
+ // files on MacOSX have an unstripped symbol table inside of them.
+ ObjectFile *symtab_objfile = symtab->GetObjectFile();
+ if (symtab_objfile && symtab_objfile->IsStripped()) {
+ ObjectFile *symfile_objfile = symfile->GetObjectFile();
+ if (symfile_objfile != symtab_objfile) {
+ Symtab *symfile_symtab = symfile_objfile->GetSymtab();
+ if (symfile_symtab) {
+ Symbol *symbol =
+ symfile_symtab->FindSymbolContainingFileAddress(
+ so_addr.GetFileAddress());
+ if (symbol && !symbol->IsSynthetic()) {
+ sc.symbol = symbol;
+ }
+ }
+ }
+ }
+ }
+ resolved_flags |= eSymbolContextSymbol;
+ }
+ }
+ }
+
+ // For function symbols, so_addr may be off by one. This is a convention
+ // consistent with FDE row indices in eh_frame sections, but requires extra
+ // logic here to permit symbol lookup for disassembly and unwind.
+ if (resolve_scope & eSymbolContextSymbol &&
+ !(resolved_flags & eSymbolContextSymbol) && resolve_tail_call_address &&
+ so_addr.IsSectionOffset()) {
+ Address previous_addr = so_addr;
+ previous_addr.Slide(-1);
+
+ bool do_resolve_tail_call_address = false; // prevent recursion
+ const uint32_t flags = ResolveSymbolContextForAddress(
+ previous_addr, resolve_scope, sc, do_resolve_tail_call_address);
+ if (flags & eSymbolContextSymbol) {
+ AddressRange addr_range;
+ if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+ false, addr_range)) {
+ if (addr_range.GetBaseAddress().GetSection() ==
+ so_addr.GetSection()) {
+ // If the requested address is one past the address range of a
+ // function (i.e. a tail call), or the decremented address is the
+ // start of a function (i.e. some forms of trampoline), indicate
+ // that the symbol has been resolved.
+ if (so_addr.GetOffset() ==
+ addr_range.GetBaseAddress().GetOffset() ||
+ so_addr.GetOffset() ==
+ addr_range.GetBaseAddress().GetOffset() +
+ addr_range.GetByteSize()) {
+ resolved_flags |= flags;
+ }
+ } else {
+ sc.symbol =
+ nullptr; // Don't trust the symbol if the sections didn't match.
+ }
+ }
+ }
+ }
+ }
+ return resolved_flags;
+}
+
+uint32_t Module::ResolveSymbolContextForFilePath(
+ const char *file_path, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ FileSpec file_spec(file_path);
+ return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+}
+
+uint32_t Module::ResolveSymbolContextsForFileSpec(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Module::ResolveSymbolContextForFilePath (%s:%u, "
+ "check_inlines = %s, resolve_scope = 0x%8.8x)",
+ file_spec.GetPath().c_str(), line,
+ check_inlines ? "yes" : "no", resolve_scope);
+
+ const uint32_t initial_count = sc_list.GetSize();
+
+ if (SymbolFile *symbols = GetSymbolFile())
+ symbols->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope,
+ sc_list);
+
+ return sc_list.GetSize() - initial_count;
+}
+
+void Module::FindGlobalVariables(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ size_t max_matches, VariableList &variables) {
+ if (SymbolFile *symbols = GetSymbolFile())
+ symbols->FindGlobalVariables(name, parent_decl_ctx, max_matches, variables);
+}
+
+void Module::FindGlobalVariables(const RegularExpression &regex,
+ size_t max_matches, VariableList &variables) {
+ SymbolFile *symbols = GetSymbolFile();
+ if (symbols)
+ symbols->FindGlobalVariables(regex, max_matches, variables);
+}
+
+void Module::FindCompileUnits(const FileSpec &path,
+ SymbolContextList &sc_list) {
+ const size_t num_compile_units = GetNumCompileUnits();
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ const bool compare_directory = (bool)path.GetDirectory();
+ for (size_t i = 0; i < num_compile_units; ++i) {
+ sc.comp_unit = GetCompileUnitAtIndex(i).get();
+ if (sc.comp_unit) {
+ if (FileSpec::Equal(*sc.comp_unit, path, compare_directory))
+ sc_list.Append(sc);
+ }
+ }
+}
+
+Module::LookupInfo::LookupInfo(ConstString name,
+ FunctionNameType name_type_mask,
+ LanguageType language)
+ : m_name(name), m_lookup_name(), m_language(language),
+ m_name_type_mask(eFunctionNameTypeNone),
+ m_match_name_after_lookup(false) {
+ const char *name_cstr = name.GetCString();
+ llvm::StringRef basename;
+ llvm::StringRef context;
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ if (CPlusPlusLanguage::IsCPPMangledName(name_cstr))
+ m_name_type_mask = eFunctionNameTypeFull;
+ else if ((language == eLanguageTypeUnknown ||
+ Language::LanguageIsObjC(language)) &&
+ ObjCLanguage::IsPossibleObjCMethodName(name_cstr))
+ m_name_type_mask = eFunctionNameTypeFull;
+ else if (Language::LanguageIsC(language)) {
+ m_name_type_mask = eFunctionNameTypeFull;
+ } else {
+ if ((language == eLanguageTypeUnknown ||
+ Language::LanguageIsObjC(language)) &&
+ ObjCLanguage::IsPossibleObjCSelector(name_cstr))
+ m_name_type_mask |= eFunctionNameTypeSelector;
+
+ CPlusPlusLanguage::MethodName cpp_method(name);
+ basename = cpp_method.GetBasename();
+ if (basename.empty()) {
+ if (CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
+ basename))
+ m_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase);
+ else
+ m_name_type_mask |= eFunctionNameTypeFull;
+ } else {
+ m_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase);
+ }
+ }
+ } else {
+ m_name_type_mask = name_type_mask;
+ if (name_type_mask & eFunctionNameTypeMethod ||
+ name_type_mask & eFunctionNameTypeBase) {
+ // If they've asked for a CPP method or function name and it can't be
+ // that, we don't even need to search for CPP methods or names.
+ CPlusPlusLanguage::MethodName cpp_method(name);
+ if (cpp_method.IsValid()) {
+ basename = cpp_method.GetBasename();
+
+ if (!cpp_method.GetQualifiers().empty()) {
+ // There is a "const" or other qualifier following the end of the
+ // function parens, this can't be a eFunctionNameTypeBase
+ m_name_type_mask &= ~(eFunctionNameTypeBase);
+ if (m_name_type_mask == eFunctionNameTypeNone)
+ return;
+ }
+ } else {
+ // If the CPP method parser didn't manage to chop this up, try to fill
+ // in the base name if we can. If a::b::c is passed in, we need to just
+ // look up "c", and then we'll filter the result later.
+ CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
+ basename);
+ }
+ }
+
+ if (name_type_mask & eFunctionNameTypeSelector) {
+ if (!ObjCLanguage::IsPossibleObjCSelector(name_cstr)) {
+ m_name_type_mask &= ~(eFunctionNameTypeSelector);
+ if (m_name_type_mask == eFunctionNameTypeNone)
+ return;
+ }
+ }
+
+ // Still try and get a basename in case someone specifies a name type mask
+ // of eFunctionNameTypeFull and a name like "A::func"
+ if (basename.empty()) {
+ if (name_type_mask & eFunctionNameTypeFull &&
+ !CPlusPlusLanguage::IsCPPMangledName(name_cstr)) {
+ CPlusPlusLanguage::MethodName cpp_method(name);
+ basename = cpp_method.GetBasename();
+ if (basename.empty())
+ CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
+ basename);
+ }
+ }
+ }
+
+ if (!basename.empty()) {
+ // The name supplied was a partial C++ path like "a::count". In this case
+ // we want to do a lookup on the basename "count" and then make sure any
+ // matching results contain "a::count" so that it would match "b::a::count"
+ // and "a::count". This is why we set "match_name_after_lookup" to true
+ m_lookup_name.SetString(basename);
+ m_match_name_after_lookup = true;
+ } else {
+ // The name is already correct, just use the exact name as supplied, and we
+ // won't need to check if any matches contain "name"
+ m_lookup_name = name;
+ m_match_name_after_lookup = false;
+ }
+}
+
+void Module::LookupInfo::Prune(SymbolContextList &sc_list,
+ size_t start_idx) const {
+ if (m_match_name_after_lookup && m_name) {
+ SymbolContext sc;
+ size_t i = start_idx;
+ while (i < sc_list.GetSize()) {
+ if (!sc_list.GetContextAtIndex(i, sc))
+ break;
+ ConstString full_name(sc.GetFunctionName());
+ if (full_name &&
+ ::strstr(full_name.GetCString(), m_name.GetCString()) == nullptr) {
+ sc_list.RemoveContextAtIndex(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ // If we have only full name matches we might have tried to set breakpoint on
+ // "func" and specified eFunctionNameTypeFull, but we might have found
+ // "a::func()", "a::b::func()", "c::func()", "func()" and "func". Only
+ // "func()" and "func" should end up matching.
+ if (m_name_type_mask == eFunctionNameTypeFull) {
+ SymbolContext sc;
+ size_t i = start_idx;
+ while (i < sc_list.GetSize()) {
+ if (!sc_list.GetContextAtIndex(i, sc))
+ break;
+ // Make sure the mangled and demangled names don't match before we try to
+ // pull anything out
+ ConstString mangled_name(sc.GetFunctionName(Mangled::ePreferMangled));
+ ConstString full_name(sc.GetFunctionName());
+ if (mangled_name != m_name && full_name != m_name)
+ {
+ CPlusPlusLanguage::MethodName cpp_method(full_name);
+ if (cpp_method.IsValid()) {
+ if (cpp_method.GetContext().empty()) {
+ if (cpp_method.GetBasename().compare(m_name.GetStringRef()) != 0) {
+ sc_list.RemoveContextAtIndex(i);
+ continue;
+ }
+ } else {
+ std::string qualified_name;
+ llvm::StringRef anon_prefix("(anonymous namespace)");
+ if (cpp_method.GetContext() == anon_prefix)
+ qualified_name = cpp_method.GetBasename().str();
+ else
+ qualified_name = cpp_method.GetScopeQualifiedName();
+ if (qualified_name != m_name.GetCString()) {
+ sc_list.RemoveContextAtIndex(i);
+ continue;
+ }
+ }
+ }
+ }
+ ++i;
+ }
+ }
+}
+
+void Module::FindFunctions(ConstString name,
+ const CompilerDeclContext *parent_decl_ctx,
+ FunctionNameType name_type_mask,
+ bool include_symbols, bool include_inlines,
+ SymbolContextList &sc_list) {
+ const size_t old_size = sc_list.GetSize();
+
+ // Find all the functions (not symbols, but debug information functions...
+ SymbolFile *symbols = GetSymbolFile();
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+
+ if (symbols) {
+ symbols->FindFunctions(lookup_info.GetLookupName(), parent_decl_ctx,
+ lookup_info.GetNameTypeMask(), include_inlines,
+ sc_list);
+
+ // Now check our symbol table for symbols that are code symbols if
+ // requested
+ if (include_symbols) {
+ Symtab *symtab = symbols->GetSymtab();
+ if (symtab)
+ symtab->FindFunctionSymbols(lookup_info.GetLookupName(),
+ lookup_info.GetNameTypeMask(), sc_list);
+ }
+ }
+
+ const size_t new_size = sc_list.GetSize();
+
+ if (old_size < new_size)
+ lookup_info.Prune(sc_list, old_size);
+ } else {
+ if (symbols) {
+ symbols->FindFunctions(name, parent_decl_ctx, name_type_mask,
+ include_inlines, sc_list);
+
+ // Now check our symbol table for symbols that are code symbols if
+ // requested
+ if (include_symbols) {
+ Symtab *symtab = symbols->GetSymtab();
+ if (symtab)
+ symtab->FindFunctionSymbols(name, name_type_mask, sc_list);
+ }
+ }
+ }
+}
+
+void Module::FindFunctions(const RegularExpression &regex, bool include_symbols,
+ bool include_inlines,
+ SymbolContextList &sc_list) {
+ const size_t start_size = sc_list.GetSize();
+
+ if (SymbolFile *symbols = GetSymbolFile()) {
+ symbols->FindFunctions(regex, include_inlines, sc_list);
+
+ // Now check our symbol table for symbols that are code symbols if
+ // requested
+ if (include_symbols) {
+ Symtab *symtab = symbols->GetSymtab();
+ if (symtab) {
+ std::vector<uint32_t> symbol_indexes;
+ symtab->AppendSymbolIndexesMatchingRegExAndType(
+ regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny,
+ symbol_indexes);
+ const size_t num_matches = symbol_indexes.size();
+ if (num_matches) {
+ SymbolContext sc(this);
+ const size_t end_functions_added_index = sc_list.GetSize();
+ size_t num_functions_added_to_sc_list =
+ end_functions_added_index - start_size;
+ if (num_functions_added_to_sc_list == 0) {
+ // No functions were added, just symbols, so we can just append
+ // them
+ for (size_t i = 0; i < num_matches; ++i) {
+ sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
+ SymbolType sym_type = sc.symbol->GetType();
+ if (sc.symbol && (sym_type == eSymbolTypeCode ||
+ sym_type == eSymbolTypeResolver))
+ sc_list.Append(sc);
+ }
+ } else {
+ typedef std::map<lldb::addr_t, uint32_t> FileAddrToIndexMap;
+ FileAddrToIndexMap file_addr_to_index;
+ for (size_t i = start_size; i < end_functions_added_index; ++i) {
+ const SymbolContext &sc = sc_list[i];
+ if (sc.block)
+ continue;
+ file_addr_to_index[sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .GetFileAddress()] = i;
+ }
+
+ FileAddrToIndexMap::const_iterator end = file_addr_to_index.end();
+ // Functions were added so we need to merge symbols into any
+ // existing function symbol contexts
+ for (size_t i = start_size; i < num_matches; ++i) {
+ sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
+ SymbolType sym_type = sc.symbol->GetType();
+ if (sc.symbol && sc.symbol->ValueIsAddress() &&
+ (sym_type == eSymbolTypeCode ||
+ sym_type == eSymbolTypeResolver)) {
+ FileAddrToIndexMap::const_iterator pos =
+ file_addr_to_index.find(
+ sc.symbol->GetAddressRef().GetFileAddress());
+ if (pos == end)
+ sc_list.Append(sc);
+ else
+ sc_list[pos->second].symbol = sc.symbol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void Module::FindAddressesForLine(const lldb::TargetSP target_sp,
+ const FileSpec &file, uint32_t line,
+ Function *function,
+ std::vector<Address> &output_local,
+ std::vector<Address> &output_extern) {
+ SearchFilterByModule filter(target_sp, m_file);
+ AddressResolverFileLine resolver(file, line, true);
+ resolver.ResolveAddress(filter);
+
+ for (size_t n = 0; n < resolver.GetNumberOfAddresses(); n++) {
+ Address addr = resolver.GetAddressRangeAtIndex(n).GetBaseAddress();
+ Function *f = addr.CalculateSymbolContextFunction();
+ if (f && f == function)
+ output_local.push_back(addr);
+ else
+ output_extern.push_back(addr);
+ }
+}
+
+void Module::FindTypes_Impl(
+ ConstString name, const CompilerDeclContext *parent_decl_ctx,
+ size_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeMap &types) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ if (SymbolFile *symbols = GetSymbolFile())
+ symbols->FindTypes(name, parent_decl_ctx, max_matches,
+ searched_symbol_files, types);
+}
+
+void Module::FindTypesInNamespace(ConstString type_name,
+ const CompilerDeclContext *parent_decl_ctx,
+ size_t max_matches, TypeList &type_list) {
+ TypeMap types_map;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ FindTypes_Impl(type_name, parent_decl_ctx, max_matches, searched_symbol_files,
+ types_map);
+ if (types_map.GetSize()) {
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ sc.SortTypeList(types_map, type_list);
+ }
+}
+
+lldb::TypeSP Module::FindFirstType(const SymbolContext &sc,
+ ConstString name, bool exact_match) {
+ TypeList type_list;
+ llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
+ FindTypes(name, exact_match, 1, searched_symbol_files, type_list);
+ if (type_list.GetSize())
+ return type_list.GetTypeAtIndex(0);
+ return TypeSP();
+}
+
+void Module::FindTypes(
+ ConstString name, bool exact_match, size_t max_matches,
+ llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
+ TypeList &types) {
+ const char *type_name_cstr = name.GetCString();
+ llvm::StringRef type_scope;
+ llvm::StringRef type_basename;
+ TypeClass type_class = eTypeClassAny;
+ TypeMap typesmap;
+
+ if (Type::GetTypeScopeAndBasename(type_name_cstr, type_scope, type_basename,
+ type_class)) {
+ // Check if "name" starts with "::" which means the qualified type starts
+ // from the root namespace and implies and exact match. The typenames we
+ // get back from clang do not start with "::" so we need to strip this off
+ // in order to get the qualified names to match
+ exact_match = type_scope.consume_front("::");
+
+ ConstString type_basename_const_str(type_basename);
+ FindTypes_Impl(type_basename_const_str, nullptr, max_matches,
+ searched_symbol_files, typesmap);
+ if (typesmap.GetSize())
+ typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class,
+ exact_match);
+ } else {
+ // The type is not in a namespace/class scope, just search for it by
+ // basename
+ if (type_class != eTypeClassAny && !type_basename.empty()) {
+ // The "type_name_cstr" will have been modified if we have a valid type
+ // class prefix (like "struct", "class", "union", "typedef" etc).
+ FindTypes_Impl(ConstString(type_basename), nullptr, UINT_MAX,
+ searched_symbol_files, typesmap);
+ typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class,
+ exact_match);
+ } else {
+ FindTypes_Impl(name, nullptr, UINT_MAX, searched_symbol_files, typesmap);
+ if (exact_match) {
+ std::string name_str(name.AsCString(""));
+ typesmap.RemoveMismatchedTypes(type_scope, name_str, type_class,
+ exact_match);
+ }
+ }
+ }
+ if (typesmap.GetSize()) {
+ SymbolContext sc;
+ sc.module_sp = shared_from_this();
+ sc.SortTypeList(typesmap, types);
+ }
+}
+
+void Module::FindTypes(llvm::ArrayRef<CompilerContext> pattern,
+ LanguageSet languages, TypeMap &types) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ if (SymbolFile *symbols = GetSymbolFile())
+ symbols->FindTypes(pattern, languages, types);
+}
+
+SymbolFile *Module::GetSymbolFile(bool can_create, Stream *feedback_strm) {
+ if (!m_did_load_symfile.load()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_load_symfile.load() && can_create) {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file != nullptr) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ m_symfile_up.reset(
+ SymbolVendor::FindPlugin(shared_from_this(), feedback_strm));
+ m_did_load_symfile = true;
+ }
+ }
+ }
+ return m_symfile_up ? m_symfile_up->GetSymbolFile() : nullptr;
+}
+
+Symtab *Module::GetSymtab() {
+ if (SymbolFile *symbols = GetSymbolFile())
+ return symbols->GetSymtab();
+ return nullptr;
+}
+
+void Module::SetFileSpecAndObjectName(const FileSpec &file,
+ ConstString object_name) {
+ // Container objects whose paths do not specify a file directly can call this
+ // function to correct the file and object names.
+ m_file = file;
+ m_mod_time = FileSystem::Instance().GetModificationTime(file);
+ m_object_name = object_name;
+}
+
+const ArchSpec &Module::GetArchitecture() const { return m_arch; }
+
+std::string Module::GetSpecificationDescription() const {
+ std::string spec(GetFileSpec().GetPath());
+ if (m_object_name) {
+ spec += '(';
+ spec += m_object_name.GetCString();
+ spec += ')';
+ }
+ return spec;
+}
+
+void Module::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (level >= eDescriptionLevelFull) {
+ if (m_arch.IsValid())
+ s->Printf("(%s) ", m_arch.GetArchitectureName());
+ }
+
+ if (level == eDescriptionLevelBrief) {
+ const char *filename = m_file.GetFilename().GetCString();
+ if (filename)
+ s->PutCString(filename);
+ } else {
+ char path[PATH_MAX];
+ if (m_file.GetPath(path, sizeof(path)))
+ s->PutCString(path);
+ }
+
+ const char *object_name = m_object_name.GetCString();
+ if (object_name)
+ s->Printf("(%s)", object_name);
+}
+
+void Module::ReportError(const char *format, ...) {
+ if (format && format[0]) {
+ StreamString strm;
+ strm.PutCString("error: ");
+ GetDescription(&strm, lldb::eDescriptionLevelBrief);
+ strm.PutChar(' ');
+ va_list args;
+ va_start(args, format);
+ strm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const int format_len = strlen(format);
+ if (format_len > 0) {
+ const char last_char = format[format_len - 1];
+ if (last_char != '\n' && last_char != '\r')
+ strm.EOL();
+ }
+ Host::SystemLog(Host::eSystemLogError, "%s", strm.GetData());
+ }
+}
+
+bool Module::FileHasChanged() const {
+ if (!m_file_has_changed)
+ m_file_has_changed =
+ (FileSystem::Instance().GetModificationTime(m_file) != m_mod_time);
+ return m_file_has_changed;
+}
+
+void Module::ReportErrorIfModifyDetected(const char *format, ...) {
+ if (!m_first_file_changed_log) {
+ if (FileHasChanged()) {
+ m_first_file_changed_log = true;
+ if (format) {
+ StreamString strm;
+ strm.PutCString("error: the object file ");
+ GetDescription(&strm, lldb::eDescriptionLevelFull);
+ strm.PutCString(" has been modified\n");
+
+ va_list args;
+ va_start(args, format);
+ strm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const int format_len = strlen(format);
+ if (format_len > 0) {
+ const char last_char = format[format_len - 1];
+ if (last_char != '\n' && last_char != '\r')
+ strm.EOL();
+ }
+ strm.PutCString("The debug session should be aborted as the original "
+ "debug information has been overwritten.\n");
+ Host::SystemLog(Host::eSystemLogError, "%s", strm.GetData());
+ }
+ }
+ }
+}
+
+void Module::ReportWarning(const char *format, ...) {
+ if (format && format[0]) {
+ StreamString strm;
+ strm.PutCString("warning: ");
+ GetDescription(&strm, lldb::eDescriptionLevelFull);
+ strm.PutChar(' ');
+
+ va_list args;
+ va_start(args, format);
+ strm.PrintfVarArg(format, args);
+ va_end(args);
+
+ const int format_len = strlen(format);
+ if (format_len > 0) {
+ const char last_char = format[format_len - 1];
+ if (last_char != '\n' && last_char != '\r')
+ strm.EOL();
+ }
+ Host::SystemLog(Host::eSystemLogWarning, "%s", strm.GetData());
+ }
+}
+
+void Module::LogMessage(Log *log, const char *format, ...) {
+ if (log != nullptr) {
+ StreamString log_message;
+ GetDescription(&log_message, lldb::eDescriptionLevelFull);
+ log_message.PutCString(": ");
+ va_list args;
+ va_start(args, format);
+ log_message.PrintfVarArg(format, args);
+ va_end(args);
+ log->PutCString(log_message.GetData());
+ }
+}
+
+void Module::LogMessageVerboseBacktrace(Log *log, const char *format, ...) {
+ if (log != nullptr) {
+ StreamString log_message;
+ GetDescription(&log_message, lldb::eDescriptionLevelFull);
+ log_message.PutCString(": ");
+ va_list args;
+ va_start(args, format);
+ log_message.PrintfVarArg(format, args);
+ va_end(args);
+ if (log->GetVerbose()) {
+ std::string back_trace;
+ llvm::raw_string_ostream stream(back_trace);
+ llvm::sys::PrintStackTrace(stream);
+ log_message.PutCString(back_trace);
+ }
+ log->PutCString(log_message.GetData());
+ }
+}
+
+void Module::Dump(Stream *s) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ s->Indent();
+ s->Printf("Module %s%s%s%s\n", m_file.GetPath().c_str(),
+ m_object_name ? "(" : "",
+ m_object_name ? m_object_name.GetCString() : "",
+ m_object_name ? ")" : "");
+
+ s->IndentMore();
+
+ ObjectFile *objfile = GetObjectFile();
+ if (objfile)
+ objfile->Dump(s);
+
+ if (SymbolFile *symbols = GetSymbolFile())
+ symbols->Dump(*s);
+
+ s->IndentLess();
+}
+
+ConstString Module::GetObjectName() const { return m_object_name; }
+
+ObjectFile *Module::GetObjectFile() {
+ if (!m_did_load_objfile.load()) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_did_load_objfile.load()) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat, "Module::GetObjectFile () module = %s",
+ GetFileSpec().GetFilename().AsCString(""));
+ DataBufferSP data_sp;
+ lldb::offset_t data_offset = 0;
+ const lldb::offset_t file_size =
+ FileSystem::Instance().GetByteSize(m_file);
+ if (file_size > m_object_offset) {
+ m_did_load_objfile = true;
+ m_objfile_sp = ObjectFile::FindPlugin(
+ shared_from_this(), &m_file, m_object_offset,
+ file_size - m_object_offset, data_sp, data_offset);
+ if (m_objfile_sp) {
+ // Once we get the object file, update our module with the object
+ // file's architecture since it might differ in vendor/os if some
+ // parts were unknown. But since the matching arch might already be
+ // more specific than the generic COFF architecture, only merge in
+ // those values that overwrite unspecified unknown values.
+ m_arch.MergeFrom(m_objfile_sp->GetArchitecture());
+ } else {
+ ReportError("failed to load objfile for %s",
+ GetFileSpec().GetPath().c_str());
+ }
+ }
+ }
+ }
+ return m_objfile_sp.get();
+}
+
+SectionList *Module::GetSectionList() {
+ // Populate m_sections_up with sections from objfile.
+ if (!m_sections_up) {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file != nullptr)
+ obj_file->CreateSections(*GetUnifiedSectionList());
+ }
+ return m_sections_up.get();
+}
+
+void Module::SectionFileAddressesChanged() {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file)
+ obj_file->SectionFileAddressesChanged();
+ if (SymbolFile *symbols = GetSymbolFile())
+ symbols->SectionFileAddressesChanged();
+}
+
+UnwindTable &Module::GetUnwindTable() {
+ if (!m_unwind_table)
+ m_unwind_table.emplace(*this);
+ return *m_unwind_table;
+}
+
+SectionList *Module::GetUnifiedSectionList() {
+ if (!m_sections_up)
+ m_sections_up = std::make_unique<SectionList>();
+ return m_sections_up.get();
+}
+
+const Symbol *Module::FindFirstSymbolWithNameAndType(ConstString name,
+ SymbolType symbol_type) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "Module::FindFirstSymbolWithNameAndType (name = %s, type = %i)",
+ name.AsCString(), symbol_type);
+ if (Symtab *symtab = GetSymtab())
+ return symtab->FindFirstSymbolWithNameAndType(
+ name, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny);
+ return nullptr;
+}
+void Module::SymbolIndicesToSymbolContextList(
+ Symtab *symtab, std::vector<uint32_t> &symbol_indexes,
+ SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ size_t num_indices = symbol_indexes.size();
+ if (num_indices > 0) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ for (size_t i = 0; i < num_indices; i++) {
+ sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]);
+ if (sc.symbol)
+ sc_list.Append(sc);
+ }
+ }
+}
+
+void Module::FindFunctionSymbols(ConstString name,
+ uint32_t name_type_mask,
+ SymbolContextList &sc_list) {
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(func_cat,
+ "Module::FindSymbolsFunctions (name = %s, mask = 0x%8.8x)",
+ name.AsCString(), name_type_mask);
+ if (Symtab *symtab = GetSymtab())
+ symtab->FindFunctionSymbols(name, name_type_mask, sc_list);
+}
+
+void Module::FindSymbolsWithNameAndType(ConstString name,
+ SymbolType symbol_type,
+ SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat, "Module::FindSymbolsWithNameAndType (name = %s, type = %i)",
+ name.AsCString(), symbol_type);
+ if (Symtab *symtab = GetSymtab()) {
+ std::vector<uint32_t> symbol_indexes;
+ symtab->FindAllSymbolsWithNameAndType(name, symbol_type, symbol_indexes);
+ SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list);
+ }
+}
+
+void Module::FindSymbolsMatchingRegExAndType(const RegularExpression &regex,
+ SymbolType symbol_type,
+ SymbolContextList &sc_list) {
+ // No need to protect this call using m_mutex all other method calls are
+ // already thread safe.
+
+ static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
+ Timer scoped_timer(
+ func_cat,
+ "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)",
+ regex.GetText().str().c_str(), symbol_type);
+ if (Symtab *symtab = GetSymtab()) {
+ std::vector<uint32_t> symbol_indexes;
+ symtab->FindAllSymbolsMatchingRexExAndType(
+ regex, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny,
+ symbol_indexes);
+ SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list);
+ }
+}
+
+void Module::PreloadSymbols() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ SymbolFile *sym_file = GetSymbolFile();
+ if (!sym_file)
+ return;
+
+ // Prime the symbol file first, since it adds symbols to the symbol table.
+ sym_file->PreloadSymbols();
+
+ // Now we can prime the symbol table.
+ if (Symtab *symtab = sym_file->GetSymtab())
+ symtab->PreloadSymbols();
+}
+
+void Module::SetSymbolFileFileSpec(const FileSpec &file) {
+ if (!FileSystem::Instance().Exists(file))
+ return;
+ if (m_symfile_up) {
+ // Remove any sections in the unified section list that come from the
+ // current symbol vendor.
+ SectionList *section_list = GetSectionList();
+ SymbolFile *symbol_file = GetSymbolFile();
+ if (section_list && symbol_file) {
+ ObjectFile *obj_file = symbol_file->GetObjectFile();
+ // Make sure we have an object file and that the symbol vendor's objfile
+ // isn't the same as the module's objfile before we remove any sections
+ // for it...
+ if (obj_file) {
+ // Check to make sure we aren't trying to specify the file we already
+ // have
+ if (obj_file->GetFileSpec() == file) {
+ // We are being told to add the exact same file that we already have
+ // we don't have to do anything.
+ return;
+ }
+
+ // Cleare the current symtab as we are going to replace it with a new
+ // one
+ obj_file->ClearSymtab();
+
+ // Clear the unwind table too, as that may also be affected by the
+ // symbol file information.
+ m_unwind_table.reset();
+
+ // The symbol file might be a directory bundle ("/tmp/a.out.dSYM")
+ // instead of a full path to the symbol file within the bundle
+ // ("/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out"). So we need to
+ // check this
+
+ if (FileSystem::Instance().IsDirectory(file)) {
+ std::string new_path(file.GetPath());
+ std::string old_path(obj_file->GetFileSpec().GetPath());
+ if (old_path.find(new_path) == 0) {
+ // We specified the same bundle as the symbol file that we already
+ // have
+ return;
+ }
+ }
+
+ if (obj_file != m_objfile_sp.get()) {
+ size_t num_sections = section_list->GetNumSections(0);
+ for (size_t idx = num_sections; idx > 0; --idx) {
+ lldb::SectionSP section_sp(
+ section_list->GetSectionAtIndex(idx - 1));
+ if (section_sp->GetObjectFile() == obj_file) {
+ section_list->DeleteSection(idx - 1);
+ }
+ }
+ }
+ }
+ }
+ // Keep all old symbol files around in case there are any lingering type
+ // references in any SBValue objects that might have been handed out.
+ m_old_symfiles.push_back(std::move(m_symfile_up));
+ }
+ m_symfile_spec = file;
+ m_symfile_up.reset();
+ m_did_load_symfile = false;
+}
+
+bool Module::IsExecutable() {
+ if (GetObjectFile() == nullptr)
+ return false;
+ else
+ return GetObjectFile()->IsExecutable();
+}
+
+bool Module::IsLoadedInTarget(Target *target) {
+ ObjectFile *obj_file = GetObjectFile();
+ if (obj_file) {
+ SectionList *sections = GetSectionList();
+ if (sections != nullptr) {
+ size_t num_sections = sections->GetSize();
+ for (size_t sect_idx = 0; sect_idx < num_sections; sect_idx++) {
+ SectionSP section_sp = sections->GetSectionAtIndex(sect_idx);
+ if (section_sp->GetLoadBaseAddress(target) != LLDB_INVALID_ADDRESS) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool Module::LoadScriptingResourceInTarget(Target *target, Status &error,
+ Stream *feedback_stream) {
+ if (!target) {
+ error.SetErrorString("invalid destination Target");
+ return false;
+ }
+
+ LoadScriptFromSymFile should_load =
+ target->TargetProperties::GetLoadScriptFromSymbolFile();
+
+ if (should_load == eLoadScriptFromSymFileFalse)
+ return false;
+
+ Debugger &debugger = target->GetDebugger();
+ const ScriptLanguage script_language = debugger.GetScriptLanguage();
+ if (script_language != eScriptLanguageNone) {
+
+ PlatformSP platform_sp(target->GetPlatform());
+
+ if (!platform_sp) {
+ error.SetErrorString("invalid Platform");
+ return false;
+ }
+
+ FileSpecList file_specs = platform_sp->LocateExecutableScriptingResources(
+ target, *this, feedback_stream);
+
+ const uint32_t num_specs = file_specs.GetSize();
+ if (num_specs) {
+ ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter();
+ if (script_interpreter) {
+ for (uint32_t i = 0; i < num_specs; ++i) {
+ FileSpec scripting_fspec(file_specs.GetFileSpecAtIndex(i));
+ if (scripting_fspec &&
+ FileSystem::Instance().Exists(scripting_fspec)) {
+ if (should_load == eLoadScriptFromSymFileWarn) {
+ if (feedback_stream)
+ feedback_stream->Printf(
+ "warning: '%s' contains a debug script. To run this script "
+ "in "
+ "this debug session:\n\n command script import "
+ "\"%s\"\n\n"
+ "To run all discovered debug scripts in this session:\n\n"
+ " settings set target.load-script-from-symbol-file "
+ "true\n",
+ GetFileSpec().GetFileNameStrippingExtension().GetCString(),
+ scripting_fspec.GetPath().c_str());
+ return false;
+ }
+ StreamString scripting_stream;
+ scripting_fspec.Dump(&scripting_stream);
+ const bool can_reload = true;
+ const bool init_lldb_globals = false;
+ bool did_load = script_interpreter->LoadScriptingModule(
+ scripting_stream.GetData(), can_reload, init_lldb_globals,
+ error);
+ if (!did_load)
+ return false;
+ }
+ }
+ } else {
+ error.SetErrorString("invalid ScriptInterpreter");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool Module::SetArchitecture(const ArchSpec &new_arch) {
+ if (!m_arch.IsValid()) {
+ m_arch = new_arch;
+ return true;
+ }
+ return m_arch.IsCompatibleMatch(new_arch);
+}
+
+bool Module::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset, bool &changed) {
+ ObjectFile *object_file = GetObjectFile();
+ if (object_file != nullptr) {
+ changed = object_file->SetLoadAddress(target, value, value_is_offset);
+ return true;
+ } else {
+ changed = false;
+ }
+ return false;
+}
+
+bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) {
+ const UUID &uuid = module_ref.GetUUID();
+
+ if (uuid.IsValid()) {
+ // If the UUID matches, then nothing more needs to match...
+ return (uuid == GetUUID());
+ }
+
+ const FileSpec &file_spec = module_ref.GetFileSpec();
+ if (file_spec) {
+ if (!FileSpec::Equal(file_spec, m_file, (bool)file_spec.GetDirectory()) &&
+ !FileSpec::Equal(file_spec, m_platform_file,
+ (bool)file_spec.GetDirectory()))
+ return false;
+ }
+
+ const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec();
+ if (platform_file_spec) {
+ if (!FileSpec::Equal(platform_file_spec, GetPlatformFileSpec(),
+ (bool)platform_file_spec.GetDirectory()))
+ return false;
+ }
+
+ const ArchSpec &arch = module_ref.GetArchitecture();
+ if (arch.IsValid()) {
+ if (!m_arch.IsCompatibleMatch(arch))
+ return false;
+ }
+
+ ConstString object_name = module_ref.GetObjectName();
+ if (object_name) {
+ if (object_name != GetObjectName())
+ return false;
+ }
+ return true;
+}
+
+bool Module::FindSourceFile(const FileSpec &orig_spec,
+ FileSpec &new_spec) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_source_mappings.FindFile(orig_spec, new_spec);
+}
+
+bool Module::RemapSourceFile(llvm::StringRef path,
+ std::string &new_path) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_source_mappings.RemapPath(path, new_path);
+}
+
+bool Module::MergeArchitecture(const ArchSpec &arch_spec) {
+ if (!arch_spec.IsValid())
+ return false;
+ LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_MODULES),
+ "module has arch %s, merging/replacing with arch %s",
+ m_arch.GetTriple().getTriple().c_str(),
+ arch_spec.GetTriple().getTriple().c_str());
+ if (!m_arch.IsCompatibleMatch(arch_spec)) {
+ // The new architecture is different, we just need to replace it.
+ return SetArchitecture(arch_spec);
+ }
+
+ // Merge bits from arch_spec into "merged_arch" and set our architecture.
+ ArchSpec merged_arch(m_arch);
+ merged_arch.MergeFrom(arch_spec);
+ // SetArchitecture() is a no-op if m_arch is already valid.
+ m_arch = ArchSpec();
+ return SetArchitecture(merged_arch);
+}
+
+llvm::VersionTuple Module::GetVersion() {
+ if (ObjectFile *obj_file = GetObjectFile())
+ return obj_file->GetVersion();
+ return llvm::VersionTuple();
+}
+
+bool Module::GetIsDynamicLinkEditor() {
+ ObjectFile *obj_file = GetObjectFile();
+
+ if (obj_file)
+ return obj_file->GetIsDynamicLinkEditor();
+
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ModuleChild.cpp b/contrib/llvm-project/lldb/source/Core/ModuleChild.cpp
new file mode 100644
index 000000000000..2fcb2ffca137
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ModuleChild.cpp
@@ -0,0 +1,28 @@
+//===-- ModuleChild.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ModuleChild.h"
+
+using namespace lldb_private;
+
+ModuleChild::ModuleChild(const lldb::ModuleSP &module_sp)
+ : m_module_wp(module_sp) {}
+
+ModuleChild::~ModuleChild() {}
+
+const ModuleChild &ModuleChild::operator=(const ModuleChild &rhs) {
+ if (this != &rhs)
+ m_module_wp = rhs.m_module_wp;
+ return *this;
+}
+
+lldb::ModuleSP ModuleChild::GetModule() const { return m_module_wp.lock(); }
+
+void ModuleChild::SetModule(const lldb::ModuleSP &module_sp) {
+ m_module_wp = module_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ModuleList.cpp b/contrib/llvm-project/lldb/source/Core/ModuleList.cpp
new file mode 100644
index 000000000000..b0567a902fd7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ModuleList.cpp
@@ -0,0 +1,1003 @@
+//===-- ModuleList.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/FileSpecList.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValueFileSpec.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/LocateSymbolFile.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-defines.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "clang/Driver/Driver.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+namespace lldb_private {
+class Function;
+}
+namespace lldb_private {
+class RegularExpression;
+}
+namespace lldb_private {
+class Stream;
+}
+namespace lldb_private {
+class SymbolFile;
+}
+namespace lldb_private {
+class Target;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+#define LLDB_PROPERTIES_modulelist
+#include "CoreProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_modulelist
+#include "CorePropertiesEnum.inc"
+};
+
+} // namespace
+
+ModuleListProperties::ModuleListProperties() {
+ m_collection_sp =
+ std::make_shared<OptionValueProperties>(ConstString("symbols"));
+ m_collection_sp->Initialize(g_modulelist_properties);
+
+ llvm::SmallString<128> path;
+ clang::driver::Driver::getDefaultModuleCachePath(path);
+ SetClangModulesCachePath(path);
+}
+
+bool ModuleListProperties::GetEnableExternalLookup() const {
+ const uint32_t idx = ePropertyEnableExternalLookup;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_modulelist_properties[idx].default_uint_value != 0);
+}
+
+bool ModuleListProperties::SetEnableExternalLookup(bool new_value) {
+ return m_collection_sp->SetPropertyAtIndexAsBoolean(
+ nullptr, ePropertyEnableExternalLookup, new_value);
+}
+
+FileSpec ModuleListProperties::GetClangModulesCachePath() const {
+ return m_collection_sp
+ ->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false,
+ ePropertyClangModulesCachePath)
+ ->GetCurrentValue();
+}
+
+bool ModuleListProperties::SetClangModulesCachePath(llvm::StringRef path) {
+ return m_collection_sp->SetPropertyAtIndexAsString(
+ nullptr, ePropertyClangModulesCachePath, path);
+}
+
+ModuleList::ModuleList()
+ : m_modules(), m_modules_mutex(), m_notifier(nullptr) {}
+
+ModuleList::ModuleList(const ModuleList &rhs)
+ : m_modules(), m_modules_mutex(), m_notifier(nullptr) {
+ std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex);
+ std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex);
+ m_modules = rhs.m_modules;
+}
+
+ModuleList::ModuleList(ModuleList::Notifier *notifier)
+ : m_modules(), m_modules_mutex(), m_notifier(notifier) {}
+
+const ModuleList &ModuleList::operator=(const ModuleList &rhs) {
+ if (this != &rhs) {
+ std::lock(m_modules_mutex, rhs.m_modules_mutex);
+ std::lock_guard<std::recursive_mutex> lhs_guard(m_modules_mutex,
+ std::adopt_lock);
+ std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_modules_mutex,
+ std::adopt_lock);
+ m_modules = rhs.m_modules;
+ }
+ return *this;
+}
+
+ModuleList::~ModuleList() = default;
+
+void ModuleList::AppendImpl(const ModuleSP &module_sp, bool use_notifier) {
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ m_modules.push_back(module_sp);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyModuleAdded(*this, module_sp);
+ }
+}
+
+void ModuleList::Append(const ModuleSP &module_sp, bool notify) {
+ AppendImpl(module_sp, notify);
+}
+
+void ModuleList::ReplaceEquivalent(const ModuleSP &module_sp) {
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+
+ // First remove any equivalent modules. Equivalent modules are modules
+ // whose path, platform path and architecture match.
+ ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(),
+ module_sp->GetArchitecture());
+ equivalent_module_spec.GetPlatformFileSpec() =
+ module_sp->GetPlatformFileSpec();
+
+ size_t idx = 0;
+ while (idx < m_modules.size()) {
+ ModuleSP module_sp(m_modules[idx]);
+ if (module_sp->MatchesModuleSpec(equivalent_module_spec))
+ RemoveImpl(m_modules.begin() + idx);
+ else
+ ++idx;
+ }
+ // Now add the new module to the list
+ Append(module_sp);
+ }
+}
+
+bool ModuleList::AppendIfNeeded(const ModuleSP &module_sp, bool notify) {
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if (pos->get() == module_sp.get())
+ return false; // Already in the list
+ }
+ // Only push module_sp on the list if it wasn't already in there.
+ Append(module_sp, notify);
+ return true;
+ }
+ return false;
+}
+
+void ModuleList::Append(const ModuleList &module_list) {
+ for (auto pos : module_list.m_modules)
+ Append(pos);
+}
+
+bool ModuleList::AppendIfNeeded(const ModuleList &module_list) {
+ bool any_in = false;
+ for (auto pos : module_list.m_modules) {
+ if (AppendIfNeeded(pos))
+ any_in = true;
+ }
+ return any_in;
+}
+
+bool ModuleList::RemoveImpl(const ModuleSP &module_sp, bool use_notifier) {
+ if (module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if (pos->get() == module_sp.get()) {
+ m_modules.erase(pos);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyModuleRemoved(*this, module_sp);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ModuleList::collection::iterator
+ModuleList::RemoveImpl(ModuleList::collection::iterator pos,
+ bool use_notifier) {
+ ModuleSP module_sp(*pos);
+ collection::iterator retval = m_modules.erase(pos);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyModuleRemoved(*this, module_sp);
+ return retval;
+}
+
+bool ModuleList::Remove(const ModuleSP &module_sp, bool notify) {
+ return RemoveImpl(module_sp, notify);
+}
+
+bool ModuleList::ReplaceModule(const lldb::ModuleSP &old_module_sp,
+ const lldb::ModuleSP &new_module_sp) {
+ if (!RemoveImpl(old_module_sp, false))
+ return false;
+ AppendImpl(new_module_sp, false);
+ if (m_notifier)
+ m_notifier->NotifyModuleUpdated(*this, old_module_sp, new_module_sp);
+ return true;
+}
+
+bool ModuleList::RemoveIfOrphaned(const Module *module_ptr) {
+ if (module_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if (pos->get() == module_ptr) {
+ if (pos->unique()) {
+ pos = RemoveImpl(pos);
+ return true;
+ } else
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+size_t ModuleList::RemoveOrphans(bool mandatory) {
+ std::unique_lock<std::recursive_mutex> lock(m_modules_mutex, std::defer_lock);
+
+ if (mandatory) {
+ lock.lock();
+ } else {
+ // Not mandatory, remove orphans if we can get the mutex
+ if (!lock.try_lock())
+ return 0;
+ }
+ collection::iterator pos = m_modules.begin();
+ size_t remove_count = 0;
+ while (pos != m_modules.end()) {
+ if (pos->unique()) {
+ pos = RemoveImpl(pos);
+ ++remove_count;
+ } else {
+ ++pos;
+ }
+ }
+ return remove_count;
+}
+
+size_t ModuleList::Remove(ModuleList &module_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ size_t num_removed = 0;
+ collection::iterator pos, end = module_list.m_modules.end();
+ for (pos = module_list.m_modules.begin(); pos != end; ++pos) {
+ if (Remove(*pos, false /* notify */))
+ ++num_removed;
+ }
+ if (m_notifier)
+ m_notifier->NotifyModulesRemoved(module_list);
+ return num_removed;
+}
+
+void ModuleList::Clear() { ClearImpl(); }
+
+void ModuleList::Destroy() { ClearImpl(); }
+
+void ModuleList::ClearImpl(bool use_notifier) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ if (use_notifier && m_notifier)
+ m_notifier->NotifyWillClearList(*this);
+ m_modules.clear();
+}
+
+Module *ModuleList::GetModulePointerAtIndex(size_t idx) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ return GetModulePointerAtIndexUnlocked(idx);
+}
+
+Module *ModuleList::GetModulePointerAtIndexUnlocked(size_t idx) const {
+ if (idx < m_modules.size())
+ return m_modules[idx].get();
+ return nullptr;
+}
+
+ModuleSP ModuleList::GetModuleAtIndex(size_t idx) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ return GetModuleAtIndexUnlocked(idx);
+}
+
+ModuleSP ModuleList::GetModuleAtIndexUnlocked(size_t idx) const {
+ ModuleSP module_sp;
+ if (idx < m_modules.size())
+ module_sp = m_modules[idx];
+ return module_sp;
+}
+
+void ModuleList::FindFunctions(ConstString name,
+ FunctionNameType name_type_mask,
+ bool include_symbols, bool include_inlines,
+ SymbolContextList &sc_list) const {
+ const size_t old_size = sc_list.GetSize();
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctions(lookup_info.GetLookupName(), nullptr,
+ lookup_info.GetNameTypeMask(), include_symbols,
+ include_inlines, sc_list);
+ }
+
+ const size_t new_size = sc_list.GetSize();
+
+ if (old_size < new_size)
+ lookup_info.Prune(sc_list, old_size);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctions(name, nullptr, name_type_mask, include_symbols,
+ include_inlines, sc_list);
+ }
+ }
+}
+
+void ModuleList::FindFunctionSymbols(ConstString name,
+ lldb::FunctionNameType name_type_mask,
+ SymbolContextList &sc_list) {
+ const size_t old_size = sc_list.GetSize();
+
+ if (name_type_mask & eFunctionNameTypeAuto) {
+ Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown);
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctionSymbols(lookup_info.GetLookupName(),
+ lookup_info.GetNameTypeMask(), sc_list);
+ }
+
+ const size_t new_size = sc_list.GetSize();
+
+ if (old_size < new_size)
+ lookup_info.Prune(sc_list, old_size);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctionSymbols(name, name_type_mask, sc_list);
+ }
+ }
+}
+
+void ModuleList::FindFunctions(const RegularExpression &name,
+ bool include_symbols, bool include_inlines,
+ SymbolContextList &sc_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindFunctions(name, include_symbols, include_inlines, sc_list);
+ }
+}
+
+void ModuleList::FindCompileUnits(const FileSpec &path,
+ SymbolContextList &sc_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindCompileUnits(path, sc_list);
+ }
+}
+
+void ModuleList::FindGlobalVariables(ConstString name, size_t max_matches,
+ VariableList &variable_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindGlobalVariables(name, nullptr, max_matches, variable_list);
+ }
+}
+
+void ModuleList::FindGlobalVariables(const RegularExpression &regex,
+ size_t max_matches,
+ VariableList &variable_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindGlobalVariables(regex, max_matches, variable_list);
+ }
+}
+
+void ModuleList::FindSymbolsWithNameAndType(ConstString name,
+ SymbolType symbol_type,
+ SymbolContextList &sc_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos)
+ (*pos)->FindSymbolsWithNameAndType(name, symbol_type, sc_list);
+}
+
+void ModuleList::FindSymbolsMatchingRegExAndType(
+ const RegularExpression &regex, lldb::SymbolType symbol_type,
+ SymbolContextList &sc_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos)
+ (*pos)->FindSymbolsMatchingRegExAndType(regex, symbol_type, sc_list);
+}
+
+void ModuleList::FindModules(const ModuleSpec &module_spec,
+ ModuleList &matching_module_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ ModuleSP module_sp(*pos);
+ if (module_sp->MatchesModuleSpec(module_spec))
+ matching_module_list.Append(module_sp);
+ }
+}
+
+ModuleSP ModuleList::FindModule(const Module *module_ptr) const {
+ ModuleSP module_sp;
+
+ // Scope for "locker"
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos).get() == module_ptr) {
+ module_sp = (*pos);
+ break;
+ }
+ }
+ }
+ return module_sp;
+}
+
+ModuleSP ModuleList::FindModule(const UUID &uuid) const {
+ ModuleSP module_sp;
+
+ if (uuid.IsValid()) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos)->GetUUID() == uuid) {
+ module_sp = (*pos);
+ break;
+ }
+ }
+ }
+ return module_sp;
+}
+
+void ModuleList::FindTypes(Module *search_first, ConstString name,
+ bool name_is_fully_qualified, size_t max_matches,
+ llvm::DenseSet<SymbolFile *> &searched_symbol_files,
+ TypeList &types) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+
+ collection::const_iterator pos, end = m_modules.end();
+ if (search_first) {
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if (search_first == pos->get()) {
+ search_first->FindTypes(name, name_is_fully_qualified, max_matches,
+ searched_symbol_files, types);
+
+ if (types.GetSize() >= max_matches)
+ return;
+ }
+ }
+ }
+
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ // Search the module if the module is not equal to the one in the symbol
+ // context "sc". If "sc" contains a empty module shared pointer, then the
+ // comparison will always be true (valid_module_ptr != nullptr).
+ if (search_first != pos->get())
+ (*pos)->FindTypes(name, name_is_fully_qualified, max_matches,
+ searched_symbol_files, types);
+
+ if (types.GetSize() >= max_matches)
+ return;
+ }
+}
+
+bool ModuleList::FindSourceFile(const FileSpec &orig_spec,
+ FileSpec &new_spec) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos)->FindSourceFile(orig_spec, new_spec))
+ return true;
+ }
+ return false;
+}
+
+void ModuleList::FindAddressesForLine(const lldb::TargetSP target_sp,
+ const FileSpec &file, uint32_t line,
+ Function *function,
+ std::vector<Address> &output_local,
+ std::vector<Address> &output_extern) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->FindAddressesForLine(target_sp, file, line, function, output_local,
+ output_extern);
+ }
+}
+
+ModuleSP ModuleList::FindFirstModule(const ModuleSpec &module_spec) const {
+ ModuleSP module_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ ModuleSP module_sp(*pos);
+ if (module_sp->MatchesModuleSpec(module_spec))
+ return module_sp;
+ }
+ return module_sp;
+}
+
+size_t ModuleList::GetSize() const {
+ size_t size = 0;
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ size = m_modules.size();
+ }
+ return size;
+}
+
+void ModuleList::Dump(Stream *s) const {
+ // s.Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ // s.Indent();
+ // s << "ModuleList\n";
+
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->Dump(s);
+ }
+}
+
+void ModuleList::LogUUIDAndPaths(Log *log, const char *prefix_cstr) {
+ if (log != nullptr) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, begin = m_modules.begin(),
+ end = m_modules.end();
+ for (pos = begin; pos != end; ++pos) {
+ Module *module = pos->get();
+ const FileSpec &module_file_spec = module->GetFileSpec();
+ LLDB_LOGF(log, "%s[%u] %s (%s) \"%s\"", prefix_cstr ? prefix_cstr : "",
+ (uint32_t)std::distance(begin, pos),
+ module->GetUUID().GetAsString().c_str(),
+ module->GetArchitecture().GetArchitectureName(),
+ module_file_spec.GetPath().c_str());
+ }
+ }
+}
+
+bool ModuleList::ResolveFileAddress(lldb::addr_t vm_addr,
+ Address &so_addr) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ if ((*pos)->ResolveFileAddress(vm_addr, so_addr))
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t
+ModuleList::ResolveSymbolContextForAddress(const Address &so_addr,
+ SymbolContextItem resolve_scope,
+ SymbolContext &sc) const {
+ // The address is already section offset so it has a module
+ uint32_t resolved_flags = 0;
+ ModuleSP module_sp(so_addr.GetModule());
+ if (module_sp) {
+ resolved_flags =
+ module_sp->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ resolved_flags =
+ (*pos)->ResolveSymbolContextForAddress(so_addr, resolve_scope, sc);
+ if (resolved_flags != 0)
+ break;
+ }
+ }
+
+ return resolved_flags;
+}
+
+uint32_t ModuleList::ResolveSymbolContextForFilePath(
+ const char *file_path, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, SymbolContextList &sc_list) const {
+ FileSpec file_spec(file_path);
+ return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+}
+
+uint32_t ModuleList::ResolveSymbolContextsForFileSpec(
+ const FileSpec &file_spec, uint32_t line, bool check_inlines,
+ SymbolContextItem resolve_scope, SymbolContextList &sc_list) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos, end = m_modules.end();
+ for (pos = m_modules.begin(); pos != end; ++pos) {
+ (*pos)->ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines,
+ resolve_scope, sc_list);
+ }
+
+ return sc_list.GetSize();
+}
+
+size_t ModuleList::GetIndexForModule(const Module *module) const {
+ if (module) {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ collection::const_iterator pos;
+ collection::const_iterator begin = m_modules.begin();
+ collection::const_iterator end = m_modules.end();
+ for (pos = begin; pos != end; ++pos) {
+ if ((*pos).get() == module)
+ return std::distance(begin, pos);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+namespace {
+struct SharedModuleListInfo {
+ ModuleList module_list;
+ ModuleListProperties module_list_properties;
+};
+}
+static SharedModuleListInfo &GetSharedModuleListInfo()
+{
+ static SharedModuleListInfo *g_shared_module_list_info = nullptr;
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ // NOTE: Intentionally leak the module list so a program doesn't have to
+ // cleanup all modules and object files as it exits. This just wastes time
+ // doing a bunch of cleanup that isn't required.
+ if (g_shared_module_list_info == nullptr)
+ g_shared_module_list_info = new SharedModuleListInfo();
+ });
+ return *g_shared_module_list_info;
+}
+
+static ModuleList &GetSharedModuleList() {
+ return GetSharedModuleListInfo().module_list;
+}
+
+ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
+ return GetSharedModuleListInfo().module_list_properties;
+}
+
+bool ModuleList::ModuleIsInCache(const Module *module_ptr) {
+ if (module_ptr) {
+ ModuleList &shared_module_list = GetSharedModuleList();
+ return shared_module_list.FindModule(module_ptr).get() != nullptr;
+ }
+ return false;
+}
+
+void ModuleList::FindSharedModules(const ModuleSpec &module_spec,
+ ModuleList &matching_module_list) {
+ GetSharedModuleList().FindModules(module_spec, matching_module_list);
+}
+
+size_t ModuleList::RemoveOrphanSharedModules(bool mandatory) {
+ return GetSharedModuleList().RemoveOrphans(mandatory);
+}
+
+Status ModuleList::GetSharedModule(const ModuleSpec &module_spec,
+ ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr,
+ ModuleSP *old_module_sp_ptr,
+ bool *did_create_ptr, bool always_create) {
+ ModuleList &shared_module_list = GetSharedModuleList();
+ std::lock_guard<std::recursive_mutex> guard(
+ shared_module_list.m_modules_mutex);
+ char path[PATH_MAX];
+
+ Status error;
+
+ module_sp.reset();
+
+ if (did_create_ptr)
+ *did_create_ptr = false;
+ if (old_module_sp_ptr)
+ old_module_sp_ptr->reset();
+
+ const UUID *uuid_ptr = module_spec.GetUUIDPtr();
+ const FileSpec &module_file_spec = module_spec.GetFileSpec();
+ const ArchSpec &arch = module_spec.GetArchitecture();
+
+ // Make sure no one else can try and get or create a module while this
+ // function is actively working on it by doing an extra lock on the global
+ // mutex list.
+ if (!always_create) {
+ ModuleList matching_module_list;
+ shared_module_list.FindModules(module_spec, matching_module_list);
+ const size_t num_matching_modules = matching_module_list.GetSize();
+
+ if (num_matching_modules > 0) {
+ for (size_t module_idx = 0; module_idx < num_matching_modules;
+ ++module_idx) {
+ module_sp = matching_module_list.GetModuleAtIndex(module_idx);
+
+ // Make sure the file for the module hasn't been modified
+ if (module_sp->FileHasChanged()) {
+ if (old_module_sp_ptr && !*old_module_sp_ptr)
+ *old_module_sp_ptr = module_sp;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_MODULES));
+ if (log != nullptr)
+ LLDB_LOGF(log,
+ "module changed: %p, removing from global module list",
+ static_cast<void *>(module_sp.get()));
+
+ shared_module_list.Remove(module_sp);
+ module_sp.reset();
+ } else {
+ // The module matches and the module was not modified from when it
+ // was last loaded.
+ return error;
+ }
+ }
+ }
+ }
+
+ if (module_sp)
+ return error;
+
+ module_sp = std::make_shared<Module>(module_spec);
+ // Make sure there are a module and an object file since we can specify a
+ // valid file path with an architecture that might not be in that file. By
+ // getting the object file we can guarantee that the architecture matches
+ if (module_sp->GetObjectFile()) {
+ // If we get in here we got the correct arch, now we just need to verify
+ // the UUID if one was given
+ if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) {
+ module_sp.reset();
+ } else {
+ if (module_sp->GetObjectFile() &&
+ module_sp->GetObjectFile()->GetType() ==
+ ObjectFile::eTypeStubLibrary) {
+ module_sp.reset();
+ } else {
+ if (did_create_ptr) {
+ *did_create_ptr = true;
+ }
+
+ shared_module_list.ReplaceEquivalent(module_sp);
+ return error;
+ }
+ }
+ } else {
+ module_sp.reset();
+ }
+
+ if (module_search_paths_ptr) {
+ const auto num_directories = module_search_paths_ptr->GetSize();
+ for (size_t idx = 0; idx < num_directories; ++idx) {
+ auto search_path_spec = module_search_paths_ptr->GetFileSpecAtIndex(idx);
+ FileSystem::Instance().Resolve(search_path_spec);
+ namespace fs = llvm::sys::fs;
+ if (!FileSystem::Instance().IsDirectory(search_path_spec))
+ continue;
+ search_path_spec.AppendPathComponent(
+ module_spec.GetFileSpec().GetFilename().AsCString());
+ if (!FileSystem::Instance().Exists(search_path_spec))
+ continue;
+
+ auto resolved_module_spec(module_spec);
+ resolved_module_spec.GetFileSpec() = search_path_spec;
+ module_sp = std::make_shared<Module>(resolved_module_spec);
+ if (module_sp->GetObjectFile()) {
+ // If we get in here we got the correct arch, now we just need to
+ // verify the UUID if one was given
+ if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) {
+ module_sp.reset();
+ } else {
+ if (module_sp->GetObjectFile()->GetType() ==
+ ObjectFile::eTypeStubLibrary) {
+ module_sp.reset();
+ } else {
+ if (did_create_ptr)
+ *did_create_ptr = true;
+
+ shared_module_list.ReplaceEquivalent(module_sp);
+ return Status();
+ }
+ }
+ } else {
+ module_sp.reset();
+ }
+ }
+ }
+
+ // Either the file didn't exist where at the path, or no path was given, so
+ // we now have to use more extreme measures to try and find the appropriate
+ // module.
+
+ // Fixup the incoming path in case the path points to a valid file, yet the
+ // arch or UUID (if one was passed in) don't match.
+ ModuleSpec located_binary_modulespec =
+ Symbols::LocateExecutableObjectFile(module_spec);
+
+ // Don't look for the file if it appears to be the same one we already
+ // checked for above...
+ if (located_binary_modulespec.GetFileSpec() != module_file_spec) {
+ if (!FileSystem::Instance().Exists(
+ located_binary_modulespec.GetFileSpec())) {
+ located_binary_modulespec.GetFileSpec().GetPath(path, sizeof(path));
+ if (path[0] == '\0')
+ module_file_spec.GetPath(path, sizeof(path));
+ // How can this check ever be true? This branch it is false, and we
+ // haven't modified file_spec.
+ if (FileSystem::Instance().Exists(
+ located_binary_modulespec.GetFileSpec())) {
+ std::string uuid_str;
+ if (uuid_ptr && uuid_ptr->IsValid())
+ uuid_str = uuid_ptr->GetAsString();
+
+ if (arch.IsValid()) {
+ if (!uuid_str.empty())
+ error.SetErrorStringWithFormat(
+ "'%s' does not contain the %s architecture and UUID %s", path,
+ arch.GetArchitectureName(), uuid_str.c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "'%s' does not contain the %s architecture.", path,
+ arch.GetArchitectureName());
+ }
+ } else {
+ error.SetErrorStringWithFormat("'%s' does not exist", path);
+ }
+ if (error.Fail())
+ module_sp.reset();
+ return error;
+ }
+
+ // Make sure no one else can try and get or create a module while this
+ // function is actively working on it by doing an extra lock on the global
+ // mutex list.
+ ModuleSpec platform_module_spec(module_spec);
+ platform_module_spec.GetFileSpec() =
+ located_binary_modulespec.GetFileSpec();
+ platform_module_spec.GetPlatformFileSpec() =
+ located_binary_modulespec.GetFileSpec();
+ platform_module_spec.GetSymbolFileSpec() =
+ located_binary_modulespec.GetSymbolFileSpec();
+ ModuleList matching_module_list;
+ shared_module_list.FindModules(platform_module_spec, matching_module_list);
+ if (!matching_module_list.IsEmpty()) {
+ module_sp = matching_module_list.GetModuleAtIndex(0);
+
+ // If we didn't have a UUID in mind when looking for the object file,
+ // then we should make sure the modification time hasn't changed!
+ if (platform_module_spec.GetUUIDPtr() == nullptr) {
+ auto file_spec_mod_time = FileSystem::Instance().GetModificationTime(
+ located_binary_modulespec.GetFileSpec());
+ if (file_spec_mod_time != llvm::sys::TimePoint<>()) {
+ if (file_spec_mod_time != module_sp->GetModificationTime()) {
+ if (old_module_sp_ptr)
+ *old_module_sp_ptr = module_sp;
+ shared_module_list.Remove(module_sp);
+ module_sp.reset();
+ }
+ }
+ }
+ }
+
+ if (!module_sp) {
+ module_sp = std::make_shared<Module>(platform_module_spec);
+ // Make sure there are a module and an object file since we can specify a
+ // valid file path with an architecture that might not be in that file.
+ // By getting the object file we can guarantee that the architecture
+ // matches
+ if (module_sp && module_sp->GetObjectFile()) {
+ if (module_sp->GetObjectFile()->GetType() ==
+ ObjectFile::eTypeStubLibrary) {
+ module_sp.reset();
+ } else {
+ if (did_create_ptr)
+ *did_create_ptr = true;
+
+ shared_module_list.ReplaceEquivalent(module_sp);
+ }
+ } else {
+ located_binary_modulespec.GetFileSpec().GetPath(path, sizeof(path));
+
+ if (located_binary_modulespec.GetFileSpec()) {
+ if (arch.IsValid())
+ error.SetErrorStringWithFormat(
+ "unable to open %s architecture in '%s'",
+ arch.GetArchitectureName(), path);
+ else
+ error.SetErrorStringWithFormat("unable to open '%s'", path);
+ } else {
+ std::string uuid_str;
+ if (uuid_ptr && uuid_ptr->IsValid())
+ uuid_str = uuid_ptr->GetAsString();
+
+ if (!uuid_str.empty())
+ error.SetErrorStringWithFormat(
+ "cannot locate a module for UUID '%s'", uuid_str.c_str());
+ else
+ error.SetErrorStringWithFormat("cannot locate a module");
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) {
+ return GetSharedModuleList().Remove(module_sp);
+}
+
+bool ModuleList::RemoveSharedModuleIfOrphaned(const Module *module_ptr) {
+ return GetSharedModuleList().RemoveIfOrphaned(module_ptr);
+}
+
+bool ModuleList::LoadScriptingResourcesInTarget(Target *target,
+ std::list<Status> &errors,
+ Stream *feedback_stream,
+ bool continue_on_error) {
+ if (!target)
+ return false;
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ for (auto module : m_modules) {
+ Status error;
+ if (module) {
+ if (!module->LoadScriptingResourceInTarget(target, error,
+ feedback_stream)) {
+ if (error.Fail() && error.AsCString()) {
+ error.SetErrorStringWithFormat("unable to load scripting data for "
+ "module %s - error reported was %s",
+ module->GetFileSpec()
+ .GetFileNameStrippingExtension()
+ .GetCString(),
+ error.AsCString());
+ errors.push_back(error);
+
+ if (!continue_on_error)
+ return false;
+ }
+ }
+ }
+ }
+ return errors.empty();
+}
+
+void ModuleList::ForEach(
+ std::function<bool(const ModuleSP &module_sp)> const &callback) const {
+ std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
+ for (const auto &module : m_modules) {
+ // If the callback returns false, then stop iterating and break out
+ if (!callback(module))
+ break;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Opcode.cpp b/contrib/llvm-project/lldb/source/Core/Opcode.cpp
new file mode 100644
index 000000000000..6ca46de40de2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Opcode.cpp
@@ -0,0 +1,140 @@
+//===-- Opcode.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Opcode.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-forward.h"
+
+#include <memory>
+
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+int Opcode::Dump(Stream *s, uint32_t min_byte_width) {
+ const uint32_t previous_bytes = s->GetWrittenBytes();
+ switch (m_type) {
+ case Opcode::eTypeInvalid:
+ s->PutCString("<invalid>");
+ break;
+ case Opcode::eType8:
+ s->Printf("0x%2.2x", m_data.inst8);
+ break;
+ case Opcode::eType16:
+ s->Printf("0x%4.4x", m_data.inst16);
+ break;
+ case Opcode::eType16_2:
+ case Opcode::eType32:
+ s->Printf("0x%8.8x", m_data.inst32);
+ break;
+
+ case Opcode::eType64:
+ s->Printf("0x%16.16" PRIx64, m_data.inst64);
+ break;
+
+ case Opcode::eTypeBytes:
+ for (uint32_t i = 0; i < m_data.inst.length; ++i) {
+ if (i > 0)
+ s->PutChar(' ');
+ s->Printf("%2.2x", m_data.inst.bytes[i]);
+ }
+ break;
+ }
+
+ uint32_t bytes_written_so_far = s->GetWrittenBytes() - previous_bytes;
+ // Add spaces to make sure bytes display comes out even in case opcodes aren't
+ // all the same size.
+ if (bytes_written_so_far < min_byte_width)
+ s->Printf("%*s", min_byte_width - bytes_written_so_far, "");
+ return s->GetWrittenBytes() - previous_bytes;
+}
+
+lldb::ByteOrder Opcode::GetDataByteOrder() const {
+ if (m_byte_order != eByteOrderInvalid) {
+ return m_byte_order;
+ }
+ switch (m_type) {
+ case Opcode::eTypeInvalid:
+ break;
+ case Opcode::eType8:
+ case Opcode::eType16:
+ case Opcode::eType16_2:
+ case Opcode::eType32:
+ case Opcode::eType64:
+ return endian::InlHostByteOrder();
+ case Opcode::eTypeBytes:
+ break;
+ }
+ return eByteOrderInvalid;
+}
+
+uint32_t Opcode::GetData(DataExtractor &data) const {
+ uint32_t byte_size = GetByteSize();
+ uint8_t swap_buf[8];
+ const void *buf = nullptr;
+
+ if (byte_size > 0) {
+ if (!GetEndianSwap()) {
+ if (m_type == Opcode::eType16_2) {
+ // 32 bit thumb instruction, we need to sizzle this a bit
+ swap_buf[0] = m_data.inst.bytes[2];
+ swap_buf[1] = m_data.inst.bytes[3];
+ swap_buf[2] = m_data.inst.bytes[0];
+ swap_buf[3] = m_data.inst.bytes[1];
+ buf = swap_buf;
+ } else {
+ buf = GetOpcodeDataBytes();
+ }
+ } else {
+ switch (m_type) {
+ case Opcode::eTypeInvalid:
+ break;
+ case Opcode::eType8:
+ buf = GetOpcodeDataBytes();
+ break;
+ case Opcode::eType16:
+ *(uint16_t *)swap_buf = llvm::ByteSwap_16(m_data.inst16);
+ buf = swap_buf;
+ break;
+ case Opcode::eType16_2:
+ swap_buf[0] = m_data.inst.bytes[1];
+ swap_buf[1] = m_data.inst.bytes[0];
+ swap_buf[2] = m_data.inst.bytes[3];
+ swap_buf[3] = m_data.inst.bytes[2];
+ buf = swap_buf;
+ break;
+ case Opcode::eType32:
+ *(uint32_t *)swap_buf = llvm::ByteSwap_32(m_data.inst32);
+ buf = swap_buf;
+ break;
+ case Opcode::eType64:
+ *(uint32_t *)swap_buf = llvm::ByteSwap_64(m_data.inst64);
+ buf = swap_buf;
+ break;
+ case Opcode::eTypeBytes:
+ buf = GetOpcodeDataBytes();
+ break;
+ }
+ }
+ }
+ if (buf != nullptr) {
+ DataBufferSP buffer_sp;
+
+ buffer_sp = std::make_shared<DataBufferHeap>(buf, byte_size);
+ data.SetByteOrder(GetDataByteOrder());
+ data.SetData(buffer_sp);
+ return byte_size;
+ }
+ data.Clear();
+ return 0;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/PluginManager.cpp b/contrib/llvm-project/lldb/source/Core/PluginManager.cpp
new file mode 100644
index 000000000000..80b64fb832fa
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/PluginManager.cpp
@@ -0,0 +1,2599 @@
+//===-- PluginManager.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/PluginManager.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/StringList.h"
+
+#if defined(_WIN32)
+#include "lldb/Host/windows/PosixApi.h"
+#endif
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <assert.h>
+
+namespace lldb_private {
+class CommandInterpreter;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum PluginAction {
+ ePluginRegisterInstance,
+ ePluginUnregisterInstance,
+ ePluginGetInstanceAtIndex
+};
+
+typedef bool (*PluginInitCallback)();
+typedef void (*PluginTermCallback)();
+
+struct PluginInfo {
+ PluginInfo() : plugin_init_callback(nullptr), plugin_term_callback(nullptr) {}
+
+ llvm::sys::DynamicLibrary library;
+ PluginInitCallback plugin_init_callback;
+ PluginTermCallback plugin_term_callback;
+};
+
+typedef std::map<FileSpec, PluginInfo> PluginTerminateMap;
+
+static std::recursive_mutex &GetPluginMapMutex() {
+ static std::recursive_mutex g_plugin_map_mutex;
+ return g_plugin_map_mutex;
+}
+
+static PluginTerminateMap &GetPluginMap() {
+ static PluginTerminateMap g_plugin_map;
+ return g_plugin_map;
+}
+
+static bool PluginIsLoaded(const FileSpec &plugin_file_spec) {
+ std::lock_guard<std::recursive_mutex> guard(GetPluginMapMutex());
+ PluginTerminateMap &plugin_map = GetPluginMap();
+ return plugin_map.find(plugin_file_spec) != plugin_map.end();
+}
+
+static void SetPluginInfo(const FileSpec &plugin_file_spec,
+ const PluginInfo &plugin_info) {
+ std::lock_guard<std::recursive_mutex> guard(GetPluginMapMutex());
+ PluginTerminateMap &plugin_map = GetPluginMap();
+ assert(plugin_map.find(plugin_file_spec) == plugin_map.end());
+ plugin_map[plugin_file_spec] = plugin_info;
+}
+
+template <typename FPtrTy> static FPtrTy CastToFPtr(void *VPtr) {
+ return reinterpret_cast<FPtrTy>(reinterpret_cast<intptr_t>(VPtr));
+}
+
+static FileSystem::EnumerateDirectoryResult
+LoadPluginCallback(void *baton, llvm::sys::fs::file_type ft,
+ llvm::StringRef path) {
+ // PluginManager *plugin_manager = (PluginManager *)baton;
+ Status error;
+
+ namespace fs = llvm::sys::fs;
+ // If we have a regular file, a symbolic link or unknown file type, try and
+ // process the file. We must handle unknown as sometimes the directory
+ // enumeration might be enumerating a file system that doesn't have correct
+ // file type information.
+ if (ft == fs::file_type::regular_file || ft == fs::file_type::symlink_file ||
+ ft == fs::file_type::type_unknown) {
+ FileSpec plugin_file_spec(path);
+ FileSystem::Instance().Resolve(plugin_file_spec);
+
+ if (PluginIsLoaded(plugin_file_spec))
+ return FileSystem::eEnumerateDirectoryResultNext;
+ else {
+ PluginInfo plugin_info;
+
+ std::string pluginLoadError;
+ plugin_info.library = llvm::sys::DynamicLibrary::getPermanentLibrary(
+ plugin_file_spec.GetPath().c_str(), &pluginLoadError);
+ if (plugin_info.library.isValid()) {
+ bool success = false;
+ plugin_info.plugin_init_callback = CastToFPtr<PluginInitCallback>(
+ plugin_info.library.getAddressOfSymbol("LLDBPluginInitialize"));
+ if (plugin_info.plugin_init_callback) {
+ // Call the plug-in "bool LLDBPluginInitialize(void)" function
+ success = plugin_info.plugin_init_callback();
+ }
+
+ if (success) {
+ // It is ok for the "LLDBPluginTerminate" symbol to be nullptr
+ plugin_info.plugin_term_callback = CastToFPtr<PluginTermCallback>(
+ plugin_info.library.getAddressOfSymbol("LLDBPluginTerminate"));
+ } else {
+ // The initialize function returned FALSE which means the plug-in
+ // might not be compatible, or might be too new or too old, or might
+ // not want to run on this machine. Set it to a default-constructed
+ // instance to invalidate it.
+ plugin_info = PluginInfo();
+ }
+
+ // Regardless of success or failure, cache the plug-in load in our
+ // plug-in info so we don't try to load it again and again.
+ SetPluginInfo(plugin_file_spec, plugin_info);
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+ }
+ }
+ }
+
+ if (ft == fs::file_type::directory_file ||
+ ft == fs::file_type::symlink_file || ft == fs::file_type::type_unknown) {
+ // Try and recurse into anything that a directory or symbolic link. We must
+ // also do this for unknown as sometimes the directory enumeration might be
+ // enumerating a file system that doesn't have correct file type
+ // information.
+ return FileSystem::eEnumerateDirectoryResultEnter;
+ }
+
+ return FileSystem::eEnumerateDirectoryResultNext;
+}
+
+void PluginManager::Initialize() {
+#if 1
+ const bool find_directories = true;
+ const bool find_files = true;
+ const bool find_other = true;
+ char dir_path[PATH_MAX];
+ if (FileSpec dir_spec = HostInfo::GetSystemPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, nullptr);
+ }
+ }
+
+ if (FileSpec dir_spec = HostInfo::GetUserPluginDir()) {
+ if (FileSystem::Instance().Exists(dir_spec) &&
+ dir_spec.GetPath(dir_path, sizeof(dir_path))) {
+ FileSystem::Instance().EnumerateDirectory(dir_path, find_directories,
+ find_files, find_other,
+ LoadPluginCallback, nullptr);
+ }
+ }
+#endif
+}
+
+void PluginManager::Terminate() {
+ std::lock_guard<std::recursive_mutex> guard(GetPluginMapMutex());
+ PluginTerminateMap &plugin_map = GetPluginMap();
+
+ PluginTerminateMap::const_iterator pos, end = plugin_map.end();
+ for (pos = plugin_map.begin(); pos != end; ++pos) {
+ // Call the plug-in "void LLDBPluginTerminate (void)" function if there is
+ // one (if the symbol was not nullptr).
+ if (pos->second.library.isValid()) {
+ if (pos->second.plugin_term_callback)
+ pos->second.plugin_term_callback();
+ }
+ }
+ plugin_map.clear();
+}
+
+#pragma mark ABI
+
+struct ABIInstance {
+ ABIInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ABICreateInstance create_callback;
+};
+
+typedef std::vector<ABIInstance> ABIInstances;
+
+static std::recursive_mutex &GetABIInstancesMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ABIInstances &GetABIInstances() {
+ static ABIInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ ABICreateInstance create_callback) {
+ if (create_callback) {
+ ABIInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ GetABIInstances().push_back(instance);
+ return true;
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(ABICreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ ABIInstances &instances = GetABIInstances();
+
+ ABIInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ABICreateInstance PluginManager::GetABICreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ ABIInstances &instances = GetABIInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ABICreateInstance
+PluginManager::GetABICreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetABIInstancesMutex());
+ ABIInstances &instances = GetABIInstances();
+
+ ABIInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark Architecture
+
+struct ArchitectureInstance {
+ ConstString name;
+ std::string description;
+ PluginManager::ArchitectureCreateInstance create_callback;
+};
+
+typedef std::vector<ArchitectureInstance> ArchitectureInstances;
+
+static std::mutex &GetArchitectureMutex() {
+ static std::mutex g_architecture_mutex;
+ return g_architecture_mutex;
+}
+
+static ArchitectureInstances &GetArchitectureInstances() {
+ static ArchitectureInstances g_instances;
+ return g_instances;
+}
+
+void PluginManager::RegisterPlugin(ConstString name,
+ llvm::StringRef description,
+ ArchitectureCreateInstance create_callback) {
+ std::lock_guard<std::mutex> guard(GetArchitectureMutex());
+ GetArchitectureInstances().push_back({name, description, create_callback});
+}
+
+void PluginManager::UnregisterPlugin(
+ ArchitectureCreateInstance create_callback) {
+ std::lock_guard<std::mutex> guard(GetArchitectureMutex());
+ auto &instances = GetArchitectureInstances();
+
+ for (auto pos = instances.begin(), end = instances.end(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return;
+ }
+ }
+ llvm_unreachable("Plugin not found");
+}
+
+std::unique_ptr<Architecture>
+PluginManager::CreateArchitectureInstance(const ArchSpec &arch) {
+ std::lock_guard<std::mutex> guard(GetArchitectureMutex());
+ for (const auto &instances : GetArchitectureInstances()) {
+ if (auto plugin_up = instances.create_callback(arch))
+ return plugin_up;
+ }
+ return nullptr;
+}
+
+#pragma mark Disassembler
+
+struct DisassemblerInstance {
+ DisassemblerInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ DisassemblerCreateInstance create_callback;
+};
+
+typedef std::vector<DisassemblerInstance> DisassemblerInstances;
+
+static std::recursive_mutex &GetDisassemblerMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static DisassemblerInstances &GetDisassemblerInstances() {
+ static DisassemblerInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ DisassemblerCreateInstance create_callback) {
+ if (create_callback) {
+ DisassemblerInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ GetDisassemblerInstances().push_back(instance);
+ return true;
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ DisassemblerCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ DisassemblerInstances &instances = GetDisassemblerInstances();
+
+ DisassemblerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+DisassemblerCreateInstance
+PluginManager::GetDisassemblerCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ DisassemblerInstances &instances = GetDisassemblerInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+DisassemblerCreateInstance
+PluginManager::GetDisassemblerCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetDisassemblerMutex());
+ DisassemblerInstances &instances = GetDisassemblerInstances();
+
+ DisassemblerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark DynamicLoader
+
+struct DynamicLoaderInstance {
+ DynamicLoaderInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ DynamicLoaderCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<DynamicLoaderInstance> DynamicLoaderInstances;
+
+static std::recursive_mutex &GetDynamicLoaderMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static DynamicLoaderInstances &GetDynamicLoaderInstances() {
+ static DynamicLoaderInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ DynamicLoaderCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ DynamicLoaderInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ GetDynamicLoaderInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ DynamicLoaderCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+
+ DynamicLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+DynamicLoaderCreateInstance
+PluginManager::GetDynamicLoaderCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+DynamicLoaderCreateInstance
+PluginManager::GetDynamicLoaderCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+
+ DynamicLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark JITLoader
+
+struct JITLoaderInstance {
+ JITLoaderInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ JITLoaderCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<JITLoaderInstance> JITLoaderInstances;
+
+static std::recursive_mutex &GetJITLoaderMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static JITLoaderInstances &GetJITLoaderInstances() {
+ static JITLoaderInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ JITLoaderCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ JITLoaderInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ GetJITLoaderInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(JITLoaderCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+
+ JITLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+JITLoaderCreateInstance
+PluginManager::GetJITLoaderCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+JITLoaderCreateInstance PluginManager::GetJITLoaderCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+
+ JITLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark EmulateInstruction
+
+struct EmulateInstructionInstance {
+ EmulateInstructionInstance()
+ : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ EmulateInstructionCreateInstance create_callback;
+};
+
+typedef std::vector<EmulateInstructionInstance> EmulateInstructionInstances;
+
+static std::recursive_mutex &GetEmulateInstructionMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static EmulateInstructionInstances &GetEmulateInstructionInstances() {
+ static EmulateInstructionInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ EmulateInstructionCreateInstance create_callback) {
+ if (create_callback) {
+ EmulateInstructionInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ GetEmulateInstructionInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ EmulateInstructionCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ EmulateInstructionInstances &instances = GetEmulateInstructionInstances();
+
+ EmulateInstructionInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+EmulateInstructionCreateInstance
+PluginManager::GetEmulateInstructionCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ EmulateInstructionInstances &instances = GetEmulateInstructionInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+EmulateInstructionCreateInstance
+PluginManager::GetEmulateInstructionCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetEmulateInstructionMutex());
+ EmulateInstructionInstances &instances = GetEmulateInstructionInstances();
+
+ EmulateInstructionInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark OperatingSystem
+
+struct OperatingSystemInstance {
+ OperatingSystemInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ OperatingSystemCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<OperatingSystemInstance> OperatingSystemInstances;
+
+static std::recursive_mutex &GetOperatingSystemMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static OperatingSystemInstances &GetOperatingSystemInstances() {
+ static OperatingSystemInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ OperatingSystemCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ OperatingSystemInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ GetOperatingSystemInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ OperatingSystemCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ OperatingSystemInstances &instances = GetOperatingSystemInstances();
+
+ OperatingSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+OperatingSystemCreateInstance
+PluginManager::GetOperatingSystemCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ OperatingSystemInstances &instances = GetOperatingSystemInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+OperatingSystemCreateInstance
+PluginManager::GetOperatingSystemCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ OperatingSystemInstances &instances = GetOperatingSystemInstances();
+
+ OperatingSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark Language
+
+struct LanguageInstance {
+ LanguageInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ LanguageCreateInstance create_callback;
+};
+
+typedef std::vector<LanguageInstance> LanguageInstances;
+
+static std::recursive_mutex &GetLanguageMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static LanguageInstances &GetLanguageInstances() {
+ static LanguageInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ LanguageCreateInstance create_callback) {
+ if (create_callback) {
+ LanguageInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ GetLanguageInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(LanguageCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ LanguageInstances &instances = GetLanguageInstances();
+
+ LanguageInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+LanguageCreateInstance
+PluginManager::GetLanguageCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ LanguageInstances &instances = GetLanguageInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+LanguageCreateInstance
+PluginManager::GetLanguageCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageMutex());
+ LanguageInstances &instances = GetLanguageInstances();
+
+ LanguageInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark LanguageRuntime
+
+struct LanguageRuntimeInstance {
+ LanguageRuntimeInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ LanguageRuntimeCreateInstance create_callback;
+ LanguageRuntimeGetCommandObject command_callback;
+ LanguageRuntimeGetExceptionPrecondition precondition_callback;
+};
+
+typedef std::vector<LanguageRuntimeInstance> LanguageRuntimeInstances;
+
+static std::recursive_mutex &GetLanguageRuntimeMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static LanguageRuntimeInstances &GetLanguageRuntimeInstances() {
+ static LanguageRuntimeInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ LanguageRuntimeCreateInstance create_callback,
+ LanguageRuntimeGetCommandObject command_callback,
+ LanguageRuntimeGetExceptionPrecondition precondition_callback) {
+ if (create_callback) {
+ LanguageRuntimeInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.command_callback = command_callback;
+ instance.precondition_callback = precondition_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ GetLanguageRuntimeInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ LanguageRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+
+ LanguageRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+LanguageRuntimeCreateInstance
+PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+LanguageRuntimeGetCommandObject
+PluginManager::GetLanguageRuntimeGetCommandObjectAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].command_callback;
+ return nullptr;
+}
+
+LanguageRuntimeGetExceptionPrecondition
+PluginManager::GetLanguageRuntimeGetExceptionPreconditionAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].precondition_callback;
+ return nullptr;
+}
+
+LanguageRuntimeCreateInstance
+PluginManager::GetLanguageRuntimeCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetLanguageRuntimeMutex());
+ LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances();
+
+ LanguageRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark SystemRuntime
+
+struct SystemRuntimeInstance {
+ SystemRuntimeInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ SystemRuntimeCreateInstance create_callback;
+};
+
+typedef std::vector<SystemRuntimeInstance> SystemRuntimeInstances;
+
+static std::recursive_mutex &GetSystemRuntimeMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static SystemRuntimeInstances &GetSystemRuntimeInstances() {
+ static SystemRuntimeInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ SystemRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ SystemRuntimeInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ GetSystemRuntimeInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ SystemRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ SystemRuntimeInstances &instances = GetSystemRuntimeInstances();
+
+ SystemRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SystemRuntimeCreateInstance
+PluginManager::GetSystemRuntimeCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ SystemRuntimeInstances &instances = GetSystemRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+SystemRuntimeCreateInstance
+PluginManager::GetSystemRuntimeCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetSystemRuntimeMutex());
+ SystemRuntimeInstances &instances = GetSystemRuntimeInstances();
+
+ SystemRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark ObjectFile
+
+struct ObjectFileInstance {
+ ObjectFileInstance()
+ : name(), description(), create_callback(nullptr),
+ create_memory_callback(nullptr), get_module_specifications(nullptr),
+ save_core(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ObjectFileCreateInstance create_callback;
+ ObjectFileCreateMemoryInstance create_memory_callback;
+ ObjectFileGetModuleSpecifications get_module_specifications;
+ ObjectFileSaveCore save_core;
+};
+
+typedef std::vector<ObjectFileInstance> ObjectFileInstances;
+
+static std::recursive_mutex &GetObjectFileMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ObjectFileInstances &GetObjectFileInstances() {
+ static ObjectFileInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ ObjectFileCreateInstance create_callback,
+ ObjectFileCreateMemoryInstance create_memory_callback,
+ ObjectFileGetModuleSpecifications get_module_specifications,
+ ObjectFileSaveCore save_core) {
+ if (create_callback) {
+ ObjectFileInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.create_memory_callback = create_memory_callback;
+ instance.save_core = save_core;
+ instance.get_module_specifications = get_module_specifications;
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ GetObjectFileInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(ObjectFileCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ObjectFileCreateInstance
+PluginManager::GetObjectFileCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ObjectFileCreateMemoryInstance
+PluginManager::GetObjectFileCreateMemoryCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+ if (idx < instances.size())
+ return instances[idx].create_memory_callback;
+ return nullptr;
+}
+
+ObjectFileGetModuleSpecifications
+PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex(
+ uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+ if (idx < instances.size())
+ return instances[idx].get_module_specifications;
+ return nullptr;
+}
+
+ObjectFileCreateInstance
+PluginManager::GetObjectFileCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+ObjectFileCreateMemoryInstance
+PluginManager::GetObjectFileCreateMemoryCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_memory_callback;
+ }
+ }
+ return nullptr;
+}
+
+Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp,
+ const FileSpec &outfile) {
+ Status error;
+ std::lock_guard<std::recursive_mutex> guard(GetObjectFileMutex());
+ ObjectFileInstances &instances = GetObjectFileInstances();
+
+ ObjectFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->save_core && pos->save_core(process_sp, outfile, error))
+ return error;
+ }
+ error.SetErrorString(
+ "no ObjectFile plugins were able to save a core for this process");
+ return error;
+}
+
+#pragma mark ObjectContainer
+
+struct ObjectContainerInstance {
+ ObjectContainerInstance()
+ : name(), description(), create_callback(nullptr),
+ get_module_specifications(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ObjectContainerCreateInstance create_callback;
+ ObjectFileGetModuleSpecifications get_module_specifications;
+};
+
+typedef std::vector<ObjectContainerInstance> ObjectContainerInstances;
+
+static std::recursive_mutex &GetObjectContainerMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ObjectContainerInstances &GetObjectContainerInstances() {
+ static ObjectContainerInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ ObjectContainerCreateInstance create_callback,
+ ObjectFileGetModuleSpecifications get_module_specifications) {
+ if (create_callback) {
+ ObjectContainerInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.get_module_specifications = get_module_specifications;
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ GetObjectContainerInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ ObjectContainerCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+
+ ObjectContainerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ObjectContainerCreateInstance
+PluginManager::GetObjectContainerCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ObjectContainerCreateInstance
+PluginManager::GetObjectContainerCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+
+ ObjectContainerInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+ObjectFileGetModuleSpecifications
+PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex(
+ uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetObjectContainerMutex());
+ ObjectContainerInstances &instances = GetObjectContainerInstances();
+ if (idx < instances.size())
+ return instances[idx].get_module_specifications;
+ return nullptr;
+}
+
+#pragma mark Platform
+
+struct PlatformInstance {
+ PlatformInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ PlatformCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<PlatformInstance> PlatformInstances;
+
+static std::recursive_mutex &GetPlatformInstancesMutex() {
+ static std::recursive_mutex g_platform_instances_mutex;
+ return g_platform_instances_mutex;
+}
+
+static PlatformInstances &GetPlatformInstances() {
+ static PlatformInstances g_platform_instances;
+ return g_platform_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ PlatformCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+
+ PlatformInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ GetPlatformInstances().push_back(instance);
+ return true;
+ }
+ return false;
+}
+
+const char *PluginManager::GetPlatformPluginNameAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ if (idx < instances.size())
+ return instances[idx].name.GetCString();
+ return nullptr;
+}
+
+const char *PluginManager::GetPlatformPluginDescriptionAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ if (idx < instances.size())
+ return instances[idx].description.c_str();
+ return nullptr;
+}
+
+bool PluginManager::UnregisterPlugin(PlatformCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+PlatformCreateInstance
+PluginManager::GetPlatformCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+PlatformCreateInstance
+PluginManager::GetPlatformCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+void PluginManager::AutoCompletePlatformName(llvm::StringRef name,
+ CompletionRequest &request) {
+ if (name.empty())
+ return;
+
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+ llvm::StringRef name_sref(name);
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ llvm::StringRef plugin_name(pos->name.GetCString());
+ if (plugin_name.startswith(name_sref))
+ request.AddCompletion(plugin_name.data());
+ }
+}
+
+#pragma mark Process
+
+struct ProcessInstance {
+ ProcessInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ ProcessCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<ProcessInstance> ProcessInstances;
+
+static std::recursive_mutex &GetProcessMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ProcessInstances &GetProcessInstances() {
+ static ProcessInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ ProcessCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ ProcessInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ GetProcessInstances().push_back(instance);
+ }
+ return false;
+}
+
+const char *PluginManager::GetProcessPluginNameAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+ if (idx < instances.size())
+ return instances[idx].name.GetCString();
+ return nullptr;
+}
+
+const char *PluginManager::GetProcessPluginDescriptionAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+ if (idx < instances.size())
+ return instances[idx].description.c_str();
+ return nullptr;
+}
+
+bool PluginManager::UnregisterPlugin(ProcessCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+
+ ProcessInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ProcessCreateInstance
+PluginManager::GetProcessCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+ProcessCreateInstance
+PluginManager::GetProcessCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+
+ ProcessInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark ScriptInterpreter
+
+struct ScriptInterpreterInstance {
+ ScriptInterpreterInstance()
+ : name(), language(lldb::eScriptLanguageNone), description(),
+ create_callback(nullptr) {}
+
+ ConstString name;
+ lldb::ScriptLanguage language;
+ std::string description;
+ ScriptInterpreterCreateInstance create_callback;
+};
+
+typedef std::vector<ScriptInterpreterInstance> ScriptInterpreterInstances;
+
+static std::recursive_mutex &GetScriptInterpreterMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static ScriptInterpreterInstances &GetScriptInterpreterInstances() {
+ static ScriptInterpreterInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ lldb::ScriptLanguage script_language,
+ ScriptInterpreterCreateInstance create_callback) {
+ if (!create_callback)
+ return false;
+ ScriptInterpreterInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.language = script_language;
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ GetScriptInterpreterInstances().push_back(instance);
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ ScriptInterpreterCreateInstance create_callback) {
+ if (!create_callback)
+ return false;
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
+
+ ScriptInterpreterInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback != create_callback)
+ continue;
+
+ instances.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+ScriptInterpreterCreateInstance
+PluginManager::GetScriptInterpreterCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+lldb::ScriptInterpreterSP
+PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
+ Debugger &debugger) {
+ std::lock_guard<std::recursive_mutex> guard(GetScriptInterpreterMutex());
+ ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
+
+ ScriptInterpreterInstances::iterator pos, end = instances.end();
+ ScriptInterpreterCreateInstance none_instance = nullptr;
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->language == lldb::eScriptLanguageNone)
+ none_instance = pos->create_callback;
+
+ if (script_lang == pos->language)
+ return pos->create_callback(debugger);
+ }
+
+ // If we didn't find one, return the ScriptInterpreter for the null language.
+ assert(none_instance != nullptr);
+ return none_instance(debugger);
+}
+
+#pragma mark -
+#pragma mark StructuredDataPlugin
+
+// StructuredDataPlugin
+
+struct StructuredDataPluginInstance {
+ StructuredDataPluginInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr), filter_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ StructuredDataPluginCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+ StructuredDataFilterLaunchInfo filter_callback;
+};
+
+typedef std::vector<StructuredDataPluginInstance> StructuredDataPluginInstances;
+
+static std::recursive_mutex &GetStructuredDataPluginMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static StructuredDataPluginInstances &GetStructuredDataPluginInstances() {
+ static StructuredDataPluginInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ StructuredDataPluginCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback,
+ StructuredDataFilterLaunchInfo filter_callback) {
+ if (create_callback) {
+ StructuredDataPluginInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ instance.filter_callback = filter_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ GetStructuredDataPluginInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ StructuredDataPluginCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances =
+ GetStructuredDataPluginInstances();
+
+ StructuredDataPluginInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+StructuredDataPluginCreateInstance
+PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances = GetStructuredDataPluginInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+StructuredDataPluginCreateInstance
+PluginManager::GetStructuredDataPluginCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances =
+ GetStructuredDataPluginInstances();
+
+ StructuredDataPluginInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+StructuredDataFilterLaunchInfo
+PluginManager::GetStructuredDataFilterCallbackAtIndex(
+ uint32_t idx, bool &iteration_complete) {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ StructuredDataPluginInstances &instances = GetStructuredDataPluginInstances();
+ if (idx < instances.size()) {
+ iteration_complete = false;
+ return instances[idx].filter_callback;
+ } else {
+ iteration_complete = true;
+ }
+ return nullptr;
+}
+
+#pragma mark SymbolFile
+
+struct SymbolFileInstance {
+ SymbolFileInstance()
+ : name(), description(), create_callback(nullptr),
+ debugger_init_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ SymbolFileCreateInstance create_callback;
+ DebuggerInitializeCallback debugger_init_callback;
+};
+
+typedef std::vector<SymbolFileInstance> SymbolFileInstances;
+
+static std::recursive_mutex &GetSymbolFileMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static SymbolFileInstances &GetSymbolFileInstances() {
+ static SymbolFileInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ SymbolFileCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ if (create_callback) {
+ SymbolFileInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.debugger_init_callback = debugger_init_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ GetSymbolFileInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(SymbolFileCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ SymbolFileInstances &instances = GetSymbolFileInstances();
+
+ SymbolFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SymbolFileCreateInstance
+PluginManager::GetSymbolFileCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ SymbolFileInstances &instances = GetSymbolFileInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+SymbolFileCreateInstance
+PluginManager::GetSymbolFileCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ SymbolFileInstances &instances = GetSymbolFileInstances();
+
+ SymbolFileInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark SymbolVendor
+
+struct SymbolVendorInstance {
+ SymbolVendorInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ SymbolVendorCreateInstance create_callback;
+};
+
+typedef std::vector<SymbolVendorInstance> SymbolVendorInstances;
+
+static std::recursive_mutex &GetSymbolVendorMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static SymbolVendorInstances &GetSymbolVendorInstances() {
+ static SymbolVendorInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name,
+ const char *description,
+ SymbolVendorCreateInstance create_callback) {
+ if (create_callback) {
+ SymbolVendorInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ GetSymbolVendorInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ SymbolVendorCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ SymbolVendorInstances &instances = GetSymbolVendorInstances();
+
+ SymbolVendorInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SymbolVendorCreateInstance
+PluginManager::GetSymbolVendorCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ SymbolVendorInstances &instances = GetSymbolVendorInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+SymbolVendorCreateInstance
+PluginManager::GetSymbolVendorCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolVendorMutex());
+ SymbolVendorInstances &instances = GetSymbolVendorInstances();
+
+ SymbolVendorInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark UnwindAssembly
+
+struct UnwindAssemblyInstance {
+ UnwindAssemblyInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ UnwindAssemblyCreateInstance create_callback;
+};
+
+typedef std::vector<UnwindAssemblyInstance> UnwindAssemblyInstances;
+
+static std::recursive_mutex &GetUnwindAssemblyMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static UnwindAssemblyInstances &GetUnwindAssemblyInstances() {
+ static UnwindAssemblyInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ UnwindAssemblyCreateInstance create_callback) {
+ if (create_callback) {
+ UnwindAssemblyInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ GetUnwindAssemblyInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ UnwindAssemblyCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances();
+
+ UnwindAssemblyInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+UnwindAssemblyCreateInstance
+PluginManager::GetUnwindAssemblyCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+UnwindAssemblyCreateInstance
+PluginManager::GetUnwindAssemblyCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetUnwindAssemblyMutex());
+ UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances();
+
+ UnwindAssemblyInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark MemoryHistory
+
+struct MemoryHistoryInstance {
+ MemoryHistoryInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ MemoryHistoryCreateInstance create_callback;
+};
+
+typedef std::vector<MemoryHistoryInstance> MemoryHistoryInstances;
+
+static std::recursive_mutex &GetMemoryHistoryMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static MemoryHistoryInstances &GetMemoryHistoryInstances() {
+ static MemoryHistoryInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ MemoryHistoryCreateInstance create_callback) {
+ if (create_callback) {
+ MemoryHistoryInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ GetMemoryHistoryInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ MemoryHistoryCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ MemoryHistoryInstances &instances = GetMemoryHistoryInstances();
+
+ MemoryHistoryInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+MemoryHistoryCreateInstance
+PluginManager::GetMemoryHistoryCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ MemoryHistoryInstances &instances = GetMemoryHistoryInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+MemoryHistoryCreateInstance
+PluginManager::GetMemoryHistoryCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetMemoryHistoryMutex());
+ MemoryHistoryInstances &instances = GetMemoryHistoryInstances();
+
+ MemoryHistoryInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark InstrumentationRuntime
+
+struct InstrumentationRuntimeInstance {
+ InstrumentationRuntimeInstance()
+ : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ InstrumentationRuntimeCreateInstance create_callback;
+ InstrumentationRuntimeGetType get_type_callback;
+};
+
+typedef std::vector<InstrumentationRuntimeInstance>
+ InstrumentationRuntimeInstances;
+
+static std::recursive_mutex &GetInstrumentationRuntimeMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static InstrumentationRuntimeInstances &GetInstrumentationRuntimeInstances() {
+ static InstrumentationRuntimeInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ InstrumentationRuntimeCreateInstance create_callback,
+ InstrumentationRuntimeGetType get_type_callback) {
+ if (create_callback) {
+ InstrumentationRuntimeInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.get_type_callback = get_type_callback;
+ std::lock_guard<std::recursive_mutex> guard(
+ GetInstrumentationRuntimeMutex());
+ GetInstrumentationRuntimeInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(
+ InstrumentationRuntimeCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+
+ InstrumentationRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+InstrumentationRuntimeGetType
+PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].get_type_callback;
+ return nullptr;
+}
+
+InstrumentationRuntimeCreateInstance
+PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+InstrumentationRuntimeCreateInstance
+PluginManager::GetInstrumentationRuntimeCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(
+ GetInstrumentationRuntimeMutex());
+ InstrumentationRuntimeInstances &instances =
+ GetInstrumentationRuntimeInstances();
+
+ InstrumentationRuntimeInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+#pragma mark TypeSystem
+
+struct TypeSystemInstance {
+ ConstString name;
+ std::string description;
+ TypeSystemCreateInstance create_callback;
+ LanguageSet supported_languages_for_types;
+ LanguageSet supported_languages_for_expressions;
+};
+
+typedef std::vector<TypeSystemInstance> TypeSystemInstances;
+
+static std::recursive_mutex &GetTypeSystemMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static TypeSystemInstances &GetTypeSystemInstances() {
+ static TypeSystemInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ TypeSystemCreateInstance create_callback,
+ LanguageSet supported_languages_for_types,
+ LanguageSet supported_languages_for_expressions) {
+ if (create_callback) {
+ TypeSystemInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.supported_languages_for_types = supported_languages_for_types;
+ instance.supported_languages_for_expressions = supported_languages_for_expressions;
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ GetTypeSystemInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(TypeSystemCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+
+ TypeSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+TypeSystemCreateInstance
+PluginManager::GetTypeSystemCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+TypeSystemCreateInstance
+PluginManager::GetTypeSystemCreateCallbackForPluginName(
+ ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+
+ TypeSystemInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+LanguageSet PluginManager::GetAllTypeSystemSupportedLanguagesForTypes() {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ LanguageSet all;
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+ for (unsigned i = 0; i < instances.size(); ++i)
+ all.bitvector |= instances[i].supported_languages_for_types.bitvector;
+ return all;
+}
+
+LanguageSet PluginManager::GetAllTypeSystemSupportedLanguagesForExpressions() {
+ std::lock_guard<std::recursive_mutex> guard(GetTypeSystemMutex());
+ LanguageSet all;
+ TypeSystemInstances &instances = GetTypeSystemInstances();
+ for (unsigned i = 0; i < instances.size(); ++i)
+ all.bitvector |= instances[i].supported_languages_for_expressions.bitvector;
+ return all;
+}
+
+#pragma mark REPL
+
+struct REPLInstance {
+ REPLInstance() : name(), description(), create_callback(nullptr) {}
+
+ ConstString name;
+ std::string description;
+ REPLCreateInstance create_callback;
+ LanguageSet supported_languages;
+};
+
+typedef std::vector<REPLInstance> REPLInstances;
+
+static std::recursive_mutex &GetREPLMutex() {
+ static std::recursive_mutex g_instances_mutex;
+ return g_instances_mutex;
+}
+
+static REPLInstances &GetREPLInstances() {
+ static REPLInstances g_instances;
+ return g_instances;
+}
+
+bool PluginManager::RegisterPlugin(ConstString name, const char *description,
+ REPLCreateInstance create_callback,
+ LanguageSet supported_languages) {
+ if (create_callback) {
+ REPLInstance instance;
+ assert((bool)name);
+ instance.name = name;
+ if (description && description[0])
+ instance.description = description;
+ instance.create_callback = create_callback;
+ instance.supported_languages = supported_languages;
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ GetREPLInstances().push_back(instance);
+ }
+ return false;
+}
+
+bool PluginManager::UnregisterPlugin(REPLCreateInstance create_callback) {
+ if (create_callback) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+
+ REPLInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->create_callback == create_callback) {
+ instances.erase(pos);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+REPLCreateInstance PluginManager::GetREPLCreateCallbackAtIndex(uint32_t idx) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+ if (idx < instances.size())
+ return instances[idx].create_callback;
+ return nullptr;
+}
+
+REPLCreateInstance
+PluginManager::GetREPLCreateCallbackForPluginName(ConstString name) {
+ if (name) {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ REPLInstances &instances = GetREPLInstances();
+
+ REPLInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (name == pos->name)
+ return pos->create_callback;
+ }
+ }
+ return nullptr;
+}
+
+LanguageSet PluginManager::GetREPLAllTypeSystemSupportedLanguages() {
+ std::lock_guard<std::recursive_mutex> guard(GetREPLMutex());
+ LanguageSet all;
+ REPLInstances &instances = GetREPLInstances();
+ for (unsigned i = 0; i < instances.size(); ++i)
+ all.bitvector |= instances[i].supported_languages.bitvector;
+ return all;
+}
+
+#pragma mark PluginManager
+
+void PluginManager::DebuggerInitialize(Debugger &debugger) {
+ // Initialize the DynamicLoader plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetDynamicLoaderMutex());
+ DynamicLoaderInstances &instances = GetDynamicLoaderInstances();
+
+ DynamicLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the JITLoader plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetJITLoaderMutex());
+ JITLoaderInstances &instances = GetJITLoaderInstances();
+
+ JITLoaderInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the Platform plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetPlatformInstancesMutex());
+ PlatformInstances &instances = GetPlatformInstances();
+
+ PlatformInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the Process plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetProcessMutex());
+ ProcessInstances &instances = GetProcessInstances();
+
+ ProcessInstances::iterator pos, end = instances.end();
+ for (pos = instances.begin(); pos != end; ++pos) {
+ if (pos->debugger_init_callback)
+ pos->debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the SymbolFile plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetSymbolFileMutex());
+ for (auto &sym_file : GetSymbolFileInstances()) {
+ if (sym_file.debugger_init_callback)
+ sym_file.debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the OperatingSystem plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetOperatingSystemMutex());
+ for (auto &os : GetOperatingSystemInstances()) {
+ if (os.debugger_init_callback)
+ os.debugger_init_callback(debugger);
+ }
+ }
+
+ // Initialize the StructuredDataPlugin plugins
+ {
+ std::lock_guard<std::recursive_mutex> guard(GetStructuredDataPluginMutex());
+ for (auto &plugin : GetStructuredDataPluginInstances()) {
+ if (plugin.debugger_init_callback)
+ plugin.debugger_init_callback(debugger);
+ }
+ }
+}
+
+// This is the preferred new way to register plugin specific settings. e.g.
+// This will put a plugin's settings under e.g.
+// "plugin.<plugin_type_name>.<plugin_type_desc>.SETTINGNAME".
+static lldb::OptionValuePropertiesSP GetDebuggerPropertyForPlugins(
+ Debugger &debugger, ConstString plugin_type_name,
+ ConstString plugin_type_desc, bool can_create) {
+ lldb::OptionValuePropertiesSP parent_properties_sp(
+ debugger.GetValueProperties());
+ if (parent_properties_sp) {
+ static ConstString g_property_name("plugin");
+
+ OptionValuePropertiesSP plugin_properties_sp =
+ parent_properties_sp->GetSubProperty(nullptr, g_property_name);
+ if (!plugin_properties_sp && can_create) {
+ plugin_properties_sp =
+ std::make_shared<OptionValueProperties>(g_property_name);
+ parent_properties_sp->AppendProperty(
+ g_property_name, ConstString("Settings specify to plugins."), true,
+ plugin_properties_sp);
+ }
+
+ if (plugin_properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp =
+ plugin_properties_sp->GetSubProperty(nullptr, plugin_type_name);
+ if (!plugin_type_properties_sp && can_create) {
+ plugin_type_properties_sp =
+ std::make_shared<OptionValueProperties>(plugin_type_name);
+ plugin_properties_sp->AppendProperty(plugin_type_name, plugin_type_desc,
+ true, plugin_type_properties_sp);
+ }
+ return plugin_type_properties_sp;
+ }
+ }
+ return lldb::OptionValuePropertiesSP();
+}
+
+// This is deprecated way to register plugin specific settings. e.g.
+// "<plugin_type_name>.plugin.<plugin_type_desc>.SETTINGNAME" and Platform
+// generic settings would be under "platform.SETTINGNAME".
+static lldb::OptionValuePropertiesSP GetDebuggerPropertyForPluginsOldStyle(
+ Debugger &debugger, ConstString plugin_type_name,
+ ConstString plugin_type_desc, bool can_create) {
+ static ConstString g_property_name("plugin");
+ lldb::OptionValuePropertiesSP parent_properties_sp(
+ debugger.GetValueProperties());
+ if (parent_properties_sp) {
+ OptionValuePropertiesSP plugin_properties_sp =
+ parent_properties_sp->GetSubProperty(nullptr, plugin_type_name);
+ if (!plugin_properties_sp && can_create) {
+ plugin_properties_sp =
+ std::make_shared<OptionValueProperties>(plugin_type_name);
+ parent_properties_sp->AppendProperty(plugin_type_name, plugin_type_desc,
+ true, plugin_properties_sp);
+ }
+
+ if (plugin_properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp =
+ plugin_properties_sp->GetSubProperty(nullptr, g_property_name);
+ if (!plugin_type_properties_sp && can_create) {
+ plugin_type_properties_sp =
+ std::make_shared<OptionValueProperties>(g_property_name);
+ plugin_properties_sp->AppendProperty(
+ g_property_name, ConstString("Settings specific to plugins"), true,
+ plugin_type_properties_sp);
+ }
+ return plugin_type_properties_sp;
+ }
+ }
+ return lldb::OptionValuePropertiesSP();
+}
+
+namespace {
+
+typedef lldb::OptionValuePropertiesSP
+GetDebuggerPropertyForPluginsPtr(Debugger &, ConstString ,
+ ConstString , bool can_create);
+
+lldb::OptionValuePropertiesSP
+GetSettingForPlugin(Debugger &debugger, ConstString setting_name,
+ ConstString plugin_type_name,
+ GetDebuggerPropertyForPluginsPtr get_debugger_property =
+ GetDebuggerPropertyForPlugins) {
+ lldb::OptionValuePropertiesSP properties_sp;
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(get_debugger_property(
+ debugger, plugin_type_name,
+ ConstString(), // not creating to so we don't need the description
+ false));
+ if (plugin_type_properties_sp)
+ properties_sp =
+ plugin_type_properties_sp->GetSubProperty(nullptr, setting_name);
+ return properties_sp;
+}
+
+bool CreateSettingForPlugin(
+ Debugger &debugger, ConstString plugin_type_name,
+ ConstString plugin_type_desc,
+ const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property,
+ GetDebuggerPropertyForPluginsPtr get_debugger_property =
+ GetDebuggerPropertyForPlugins) {
+ if (properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(
+ get_debugger_property(debugger, plugin_type_name, plugin_type_desc,
+ true));
+ if (plugin_type_properties_sp) {
+ plugin_type_properties_sp->AppendProperty(properties_sp->GetName(),
+ description, is_global_property,
+ properties_sp);
+ return true;
+ }
+ }
+ return false;
+}
+
+const char *kDynamicLoaderPluginName("dynamic-loader");
+const char *kPlatformPluginName("platform");
+const char *kProcessPluginName("process");
+const char *kSymbolFilePluginName("symbol-file");
+const char *kJITLoaderPluginName("jit-loader");
+const char *kStructuredDataPluginName("structured-data");
+
+} // anonymous namespace
+
+lldb::OptionValuePropertiesSP PluginManager::GetSettingForDynamicLoaderPlugin(
+ Debugger &debugger, ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kDynamicLoaderPluginName));
+}
+
+bool PluginManager::CreateSettingForDynamicLoaderPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(
+ debugger, ConstString(kDynamicLoaderPluginName),
+ ConstString("Settings for dynamic loader plug-ins"), properties_sp,
+ description, is_global_property);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForPlatformPlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kPlatformPluginName),
+ GetDebuggerPropertyForPluginsOldStyle);
+}
+
+bool PluginManager::CreateSettingForPlatformPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, ConstString(kPlatformPluginName),
+ ConstString("Settings for platform plug-ins"),
+ properties_sp, description, is_global_property,
+ GetDebuggerPropertyForPluginsOldStyle);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForProcessPlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kProcessPluginName));
+}
+
+bool PluginManager::CreateSettingForProcessPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, ConstString(kProcessPluginName),
+ ConstString("Settings for process plug-ins"),
+ properties_sp, description, is_global_property);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForSymbolFilePlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kSymbolFilePluginName));
+}
+
+bool PluginManager::CreateSettingForSymbolFilePlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(
+ debugger, ConstString(kSymbolFilePluginName),
+ ConstString("Settings for symbol file plug-ins"), properties_sp,
+ description, is_global_property);
+}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForJITLoaderPlugin(Debugger &debugger,
+ ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kJITLoaderPluginName));
+}
+
+bool PluginManager::CreateSettingForJITLoaderPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, ConstString(kJITLoaderPluginName),
+ ConstString("Settings for JIT loader plug-ins"),
+ properties_sp, description, is_global_property);
+}
+
+static const char *kOperatingSystemPluginName("os");
+
+lldb::OptionValuePropertiesSP PluginManager::GetSettingForOperatingSystemPlugin(
+ Debugger &debugger, ConstString setting_name) {
+ lldb::OptionValuePropertiesSP properties_sp;
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(
+ GetDebuggerPropertyForPlugins(
+ debugger, ConstString(kOperatingSystemPluginName),
+ ConstString(), // not creating to so we don't need the description
+ false));
+ if (plugin_type_properties_sp)
+ properties_sp =
+ plugin_type_properties_sp->GetSubProperty(nullptr, setting_name);
+ return properties_sp;
+}
+
+bool PluginManager::CreateSettingForOperatingSystemPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ if (properties_sp) {
+ lldb::OptionValuePropertiesSP plugin_type_properties_sp(
+ GetDebuggerPropertyForPlugins(
+ debugger, ConstString(kOperatingSystemPluginName),
+ ConstString("Settings for operating system plug-ins"), true));
+ if (plugin_type_properties_sp) {
+ plugin_type_properties_sp->AppendProperty(properties_sp->GetName(),
+ description, is_global_property,
+ properties_sp);
+ return true;
+ }
+ }
+ return false;
+}
+
+lldb::OptionValuePropertiesSP PluginManager::GetSettingForStructuredDataPlugin(
+ Debugger &debugger, ConstString setting_name) {
+ return GetSettingForPlugin(debugger, setting_name,
+ ConstString(kStructuredDataPluginName));
+}
+
+bool PluginManager::CreateSettingForStructuredDataPlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ ConstString description, bool is_global_property) {
+ return CreateSettingForPlugin(
+ debugger, ConstString(kStructuredDataPluginName),
+ ConstString("Settings for structured data plug-ins"), properties_sp,
+ description, is_global_property);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp b/contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp
new file mode 100644
index 000000000000..3d1941ebdd26
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/RichManglingContext.cpp
@@ -0,0 +1,172 @@
+//===-- RichManglingContext.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/RichManglingContext.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+
+#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
+
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// RichManglingContext
+void RichManglingContext::ResetProvider(InfoProvider new_provider) {
+ // If we want to support parsers for other languages some day, we need a
+ // switch here to delete the correct parser type.
+ if (m_cxx_method_parser.hasValue()) {
+ assert(m_provider == PluginCxxLanguage);
+ delete get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser);
+ m_cxx_method_parser.reset();
+ }
+
+ assert(new_provider != None && "Only reset to a valid provider");
+ m_provider = new_provider;
+}
+
+bool RichManglingContext::FromItaniumName(ConstString mangled) {
+ bool err = m_ipd.partialDemangle(mangled.GetCString());
+ if (!err) {
+ ResetProvider(ItaniumPartialDemangler);
+ }
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) {
+ if (!err) {
+ ParseFullName();
+ LLDB_LOG(log, "demangled itanium: {0} -> \"{1}\"", mangled, m_ipd_buf);
+ } else {
+ LLDB_LOG(log, "demangled itanium: {0} -> error: failed to demangle",
+ mangled);
+ }
+ }
+
+ return !err; // true == success
+}
+
+bool RichManglingContext::FromCxxMethodName(ConstString demangled) {
+ ResetProvider(PluginCxxLanguage);
+ m_cxx_method_parser = new CPlusPlusLanguage::MethodName(demangled);
+ return true;
+}
+
+bool RichManglingContext::IsCtorOrDtor() const {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler:
+ return m_ipd.isCtorOrDtor();
+ case PluginCxxLanguage: {
+ // We can only check for destructors here.
+ auto base_name =
+ get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->GetBasename();
+ return base_name.startswith("~");
+ }
+ case None:
+ return false;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+bool RichManglingContext::IsFunction() const {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler:
+ return m_ipd.isFunction();
+ case PluginCxxLanguage:
+ return get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->IsValid();
+ case None:
+ return false;
+ }
+ llvm_unreachable("Fully covered switch above!");
+}
+
+void RichManglingContext::processIPDStrResult(char *ipd_res, size_t res_size) {
+ // Error case: Clear the buffer.
+ if (LLVM_UNLIKELY(ipd_res == nullptr)) {
+ assert(res_size == m_ipd_buf_size &&
+ "Failed IPD queries keep the original size in the N parameter");
+
+ m_ipd_buf[0] = '\0';
+ m_buffer = llvm::StringRef(m_ipd_buf, 0);
+ return;
+ }
+
+ // IPD's res_size includes null terminator.
+ assert(ipd_res[res_size - 1] == '\0' &&
+ "IPD returns null-terminated strings and we rely on that");
+
+ // Update buffer/size on realloc.
+ if (LLVM_UNLIKELY(ipd_res != m_ipd_buf || res_size > m_ipd_buf_size)) {
+ m_ipd_buf = ipd_res; // std::realloc freed or reused the old buffer.
+ m_ipd_buf_size = res_size; // May actually be bigger, but we can't know.
+
+ if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE))
+ LLDB_LOG(log, "ItaniumPartialDemangler Realloc: new buffer size is {0}",
+ m_ipd_buf_size);
+ }
+
+ // 99% case: Just remember the string length.
+ m_buffer = llvm::StringRef(m_ipd_buf, res_size - 1);
+}
+
+void RichManglingContext::ParseFunctionBaseName() {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler: {
+ auto n = m_ipd_buf_size;
+ auto buf = m_ipd.getFunctionBaseName(m_ipd_buf, &n);
+ processIPDStrResult(buf, n);
+ return;
+ }
+ case PluginCxxLanguage:
+ m_buffer =
+ get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->GetBasename();
+ return;
+ case None:
+ return;
+ }
+}
+
+void RichManglingContext::ParseFunctionDeclContextName() {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler: {
+ auto n = m_ipd_buf_size;
+ auto buf = m_ipd.getFunctionDeclContextName(m_ipd_buf, &n);
+ processIPDStrResult(buf, n);
+ return;
+ }
+ case PluginCxxLanguage:
+ m_buffer =
+ get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)->GetContext();
+ return;
+ case None:
+ return;
+ }
+}
+
+void RichManglingContext::ParseFullName() {
+ assert(m_provider != None && "Initialize a provider first");
+ switch (m_provider) {
+ case ItaniumPartialDemangler: {
+ auto n = m_ipd_buf_size;
+ auto buf = m_ipd.finishDemangle(m_ipd_buf, &n);
+ processIPDStrResult(buf, n);
+ return;
+ }
+ case PluginCxxLanguage:
+ m_buffer = get<CPlusPlusLanguage::MethodName>(m_cxx_method_parser)
+ ->GetFullName()
+ .GetStringRef();
+ return;
+ case None:
+ return;
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Core/SearchFilter.cpp b/contrib/llvm-project/lldb/source/Core/SearchFilter.cpp
new file mode 100644
index 000000000000..e02b4f66b58c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/SearchFilter.cpp
@@ -0,0 +1,852 @@
+//===-- SearchFilter.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/SearchFilter.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <inttypes.h>
+#include <string.h>
+
+namespace lldb_private {
+class Address;
+}
+namespace lldb_private {
+class Function;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char *SearchFilter::g_ty_to_name[] = {"Unconstrained", "Exception",
+ "Module", "Modules",
+ "ModulesAndCU", "Unknown"};
+
+const char
+ *SearchFilter::g_option_names[SearchFilter::OptionNames::LastOptionName] = {
+ "ModuleList", "CUList"};
+
+const char *SearchFilter::FilterTyToName(enum FilterTy type) {
+ if (type > LastKnownFilterType)
+ return g_ty_to_name[UnknownFilter];
+
+ return g_ty_to_name[type];
+}
+
+SearchFilter::FilterTy SearchFilter::NameToFilterTy(llvm::StringRef name) {
+ for (size_t i = 0; i <= LastKnownFilterType; i++) {
+ if (name == g_ty_to_name[i])
+ return (FilterTy)i;
+ }
+ return UnknownFilter;
+}
+
+Searcher::Searcher() = default;
+
+Searcher::~Searcher() = default;
+
+void Searcher::GetDescription(Stream *s) {}
+
+SearchFilter::SearchFilter(const TargetSP &target_sp, unsigned char filterType)
+ : m_target_sp(target_sp), SubclassID(filterType) {}
+
+SearchFilter::~SearchFilter() = default;
+
+SearchFilterSP SearchFilter::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &filter_dict,
+ Status &error) {
+ SearchFilterSP result_sp;
+ if (!filter_dict.IsValid()) {
+ error.SetErrorString("Can't deserialize from an invalid data object.");
+ return result_sp;
+ }
+
+ llvm::StringRef subclass_name;
+
+ bool success = filter_dict.GetValueForKeyAsString(
+ GetSerializationSubclassKey(), subclass_name);
+ if (!success) {
+ error.SetErrorStringWithFormat("Filter data missing subclass key");
+ return result_sp;
+ }
+
+ FilterTy filter_type = NameToFilterTy(subclass_name);
+ if (filter_type == UnknownFilter) {
+ error.SetErrorStringWithFormatv("Unknown filter type: {0}.", subclass_name);
+ return result_sp;
+ }
+
+ StructuredData::Dictionary *subclass_options = nullptr;
+ success = filter_dict.GetValueForKeyAsDictionary(
+ GetSerializationSubclassOptionsKey(), subclass_options);
+ if (!success || !subclass_options || !subclass_options->IsValid()) {
+ error.SetErrorString("Filter data missing subclass options key.");
+ return result_sp;
+ }
+
+ switch (filter_type) {
+ case Unconstrained:
+ result_sp = SearchFilterForUnconstrainedSearches::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case ByModule:
+ result_sp = SearchFilterByModule::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case ByModules:
+ result_sp = SearchFilterByModuleList::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case ByModulesAndCU:
+ result_sp = SearchFilterByModuleListAndCU::CreateFromStructuredData(
+ target, *subclass_options, error);
+ break;
+ case Exception:
+ error.SetErrorString("Can't serialize exception breakpoints yet.");
+ break;
+ default:
+ llvm_unreachable("Should never get an uresolvable filter type.");
+ }
+
+ return result_sp;
+}
+
+bool SearchFilter::ModulePasses(const FileSpec &spec) { return true; }
+
+bool SearchFilter::ModulePasses(const ModuleSP &module_sp) { return true; }
+
+bool SearchFilter::AddressPasses(Address &address) { return true; }
+
+bool SearchFilter::CompUnitPasses(FileSpec &fileSpec) { return true; }
+
+bool SearchFilter::CompUnitPasses(CompileUnit &compUnit) { return true; }
+
+bool SearchFilter::FunctionPasses(Function &function) {
+ // This is a slightly cheesy job, but since we don't have finer grained
+ // filters yet, just checking that the start address passes is probably
+ // good enough for the base class behavior.
+ Address addr = function.GetAddressRange().GetBaseAddress();
+ return AddressPasses(addr);
+}
+
+
+uint32_t SearchFilter::GetFilterRequiredItems() {
+ return (lldb::SymbolContextItem)0;
+}
+
+void SearchFilter::GetDescription(Stream *s) {}
+
+void SearchFilter::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP SearchFilter::CopyForBreakpoint(Breakpoint &breakpoint) {
+ SearchFilterSP ret_sp = DoCopyForBreakpoint(breakpoint);
+ TargetSP target_sp = breakpoint.GetTargetSP();
+ ret_sp->SetTarget(target_sp);
+ return ret_sp;
+}
+
+// Helper functions for serialization.
+
+StructuredData::DictionarySP
+SearchFilter::WrapOptionsDict(StructuredData::DictionarySP options_dict_sp) {
+ if (!options_dict_sp || !options_dict_sp->IsValid())
+ return StructuredData::DictionarySP();
+
+ auto type_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetFilterName());
+ type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
+
+ return type_dict_sp;
+}
+
+void SearchFilter::SerializeFileSpecList(
+ StructuredData::DictionarySP &options_dict_sp, OptionNames name,
+ FileSpecList &file_list) {
+ size_t num_modules = file_list.GetSize();
+
+ // Don't serialize empty lists.
+ if (num_modules == 0)
+ return;
+
+ auto module_array_sp = std::make_shared<StructuredData::Array>();
+ for (size_t i = 0; i < num_modules; i++) {
+ module_array_sp->AddItem(std::make_shared<StructuredData::String>(
+ file_list.GetFileSpecAtIndex(i).GetPath()));
+ }
+ options_dict_sp->AddItem(GetKey(name), module_array_sp);
+}
+
+// UTILITY Functions to help iterate down through the elements of the
+// SymbolContext.
+
+void SearchFilter::Search(Searcher &searcher) {
+ SymbolContext empty_sc;
+
+ if (!m_target_sp)
+ return;
+ empty_sc.target_sp = m_target_sp;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget)
+ searcher.SearchCallback(*this, empty_sc, nullptr);
+ else
+ DoModuleIteration(empty_sc, searcher);
+}
+
+void SearchFilter::SearchInModuleList(Searcher &searcher, ModuleList &modules) {
+ SymbolContext empty_sc;
+
+ if (!m_target_sp)
+ return;
+ empty_sc.target_sp = m_target_sp;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget)
+ searcher.SearchCallback(*this, empty_sc, nullptr);
+ else {
+ std::lock_guard<std::recursive_mutex> guard(modules.GetMutex());
+ const size_t numModules = modules.GetSize();
+
+ for (size_t i = 0; i < numModules; i++) {
+ ModuleSP module_sp(modules.GetModuleAtIndexUnlocked(i));
+ if (ModulePasses(module_sp)) {
+ if (DoModuleIteration(module_sp, searcher) ==
+ Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+ }
+}
+
+Searcher::CallbackReturn
+SearchFilter::DoModuleIteration(const lldb::ModuleSP &module_sp,
+ Searcher &searcher) {
+ SymbolContext matchingContext(m_target_sp, module_sp);
+ return DoModuleIteration(matchingContext, searcher);
+}
+
+Searcher::CallbackReturn
+SearchFilter::DoModuleIteration(const SymbolContext &context,
+ Searcher &searcher) {
+ if (searcher.GetDepth() >= lldb::eSearchDepthModule) {
+ if (context.module_sp) {
+ if (searcher.GetDepth() == lldb::eSearchDepthModule) {
+ SymbolContext matchingContext(context.module_sp.get());
+ searcher.SearchCallback(*this, matchingContext, nullptr);
+ } else {
+ return DoCUIteration(context.module_sp, context, searcher);
+ }
+ } else {
+ const ModuleList &target_images = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ size_t n_modules = target_images.GetSize();
+ for (size_t i = 0; i < n_modules; i++) {
+ // If this is the last level supplied, then call the callback directly,
+ // otherwise descend.
+ ModuleSP module_sp(target_images.GetModuleAtIndexUnlocked(i));
+ if (!ModulePasses(module_sp))
+ continue;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthModule) {
+ SymbolContext matchingContext(m_target_sp, module_sp);
+
+ Searcher::CallbackReturn shouldContinue =
+ searcher.SearchCallback(*this, matchingContext, nullptr);
+ if (shouldContinue == Searcher::eCallbackReturnStop ||
+ shouldContinue == Searcher::eCallbackReturnPop)
+ return shouldContinue;
+ } else {
+ Searcher::CallbackReturn shouldContinue =
+ DoCUIteration(module_sp, context, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return shouldContinue;
+ else if (shouldContinue == Searcher::eCallbackReturnPop)
+ continue;
+ }
+ }
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn
+SearchFilter::DoCUIteration(const ModuleSP &module_sp,
+ const SymbolContext &context, Searcher &searcher) {
+ Searcher::CallbackReturn shouldContinue;
+ if (context.comp_unit == nullptr) {
+ const size_t num_comp_units = module_sp->GetNumCompileUnits();
+ for (size_t i = 0; i < num_comp_units; i++) {
+ CompUnitSP cu_sp(module_sp->GetCompileUnitAtIndex(i));
+ if (cu_sp) {
+ if (!CompUnitPasses(*(cu_sp.get())))
+ continue;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthCompUnit) {
+ SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get());
+
+ shouldContinue =
+ searcher.SearchCallback(*this, matchingContext, nullptr);
+
+ if (shouldContinue == Searcher::eCallbackReturnPop)
+ return Searcher::eCallbackReturnContinue;
+ else if (shouldContinue == Searcher::eCallbackReturnStop)
+ return shouldContinue;
+ } else {
+ // First make sure this compile unit's functions are parsed
+ // since CompUnit::ForeachFunction only iterates over already
+ // parsed functions.
+ SymbolFile *sym_file = module_sp->GetSymbolFile();
+ if (!sym_file)
+ continue;
+ if (!sym_file->ParseFunctions(*cu_sp))
+ continue;
+ // If we got any functions, use ForeachFunction to do the iteration.
+ cu_sp->ForeachFunction([&](const FunctionSP &func_sp) {
+ if (!FunctionPasses(*func_sp.get()))
+ return false; // Didn't pass the filter, just keep going.
+ if (searcher.GetDepth() == lldb::eSearchDepthFunction) {
+ SymbolContext matchingContext(m_target_sp, module_sp,
+ cu_sp.get(), func_sp.get());
+ shouldContinue =
+ searcher.SearchCallback(*this, matchingContext, nullptr);
+ } else {
+ shouldContinue = DoFunctionIteration(func_sp.get(), context,
+ searcher);
+ }
+ return shouldContinue != Searcher::eCallbackReturnContinue;
+ });
+ }
+ }
+ }
+ } else {
+ if (CompUnitPasses(*context.comp_unit)) {
+ SymbolContext matchingContext(m_target_sp, module_sp, context.comp_unit);
+ return searcher.SearchCallback(*this, matchingContext, nullptr);
+ }
+ }
+ return Searcher::eCallbackReturnContinue;
+}
+
+Searcher::CallbackReturn SearchFilter::DoFunctionIteration(
+ Function *function, const SymbolContext &context, Searcher &searcher) {
+ // FIXME: Implement...
+ return Searcher::eCallbackReturnContinue;
+}
+
+// SearchFilterForUnconstrainedSearches:
+// Selects a shared library matching a given file spec, consulting the targets
+// "black list".
+SearchFilterSP SearchFilterForUnconstrainedSearches::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ // No options for an unconstrained search.
+ return std::make_shared<SearchFilterForUnconstrainedSearches>(
+ target.shared_from_this());
+}
+
+StructuredData::ObjectSP
+SearchFilterForUnconstrainedSearches::SerializeToStructuredData() {
+ // The options dictionary is an empty dictionary:
+ auto result_sp = std::make_shared<StructuredData::Dictionary>();
+ return WrapOptionsDict(result_sp);
+}
+
+bool SearchFilterForUnconstrainedSearches::ModulePasses(
+ const FileSpec &module_spec) {
+ return !m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_spec);
+}
+
+bool SearchFilterForUnconstrainedSearches::ModulePasses(
+ const lldb::ModuleSP &module_sp) {
+ if (!module_sp)
+ return true;
+ else if (m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_sp))
+ return false;
+ else
+ return true;
+}
+
+lldb::SearchFilterSP SearchFilterForUnconstrainedSearches::DoCopyForBreakpoint(
+ Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterForUnconstrainedSearches>(*this);
+}
+
+// SearchFilterByModule:
+// Selects a shared library matching a given file spec
+
+SearchFilterByModule::SearchFilterByModule(const lldb::TargetSP &target_sp,
+ const FileSpec &module)
+ : SearchFilter(target_sp, FilterTy::ByModule), m_module_spec(module) {}
+
+SearchFilterByModule::~SearchFilterByModule() = default;
+
+bool SearchFilterByModule::ModulePasses(const ModuleSP &module_sp) {
+ return (module_sp &&
+ FileSpec::Equal(module_sp->GetFileSpec(), m_module_spec, false));
+}
+
+bool SearchFilterByModule::ModulePasses(const FileSpec &spec) {
+ // Do a full match only if "spec" has a directory
+ const bool full_match = (bool)spec.GetDirectory();
+ return FileSpec::Equal(spec, m_module_spec, full_match);
+}
+
+bool SearchFilterByModule::AddressPasses(Address &address) {
+ // FIXME: Not yet implemented
+ return true;
+}
+
+bool SearchFilterByModule::CompUnitPasses(FileSpec &fileSpec) { return true; }
+
+bool SearchFilterByModule::CompUnitPasses(CompileUnit &compUnit) {
+ return true;
+}
+
+void SearchFilterByModule::Search(Searcher &searcher) {
+ if (!m_target_sp)
+ return;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget) {
+ SymbolContext empty_sc;
+ empty_sc.target_sp = m_target_sp;
+ searcher.SearchCallback(*this, empty_sc, nullptr);
+ }
+
+ // If the module file spec is a full path, then we can just find the one
+ // filespec that passes. Otherwise, we need to go through all modules and
+ // find the ones that match the file name.
+
+ const ModuleList &target_modules = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+
+ const size_t num_modules = target_modules.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ Module *module = target_modules.GetModulePointerAtIndexUnlocked(i);
+ const bool full_match = (bool)m_module_spec.GetDirectory();
+ if (FileSpec::Equal(m_module_spec, module->GetFileSpec(), full_match)) {
+ SymbolContext matchingContext(m_target_sp, module->shared_from_this());
+ Searcher::CallbackReturn shouldContinue;
+
+ shouldContinue = DoModuleIteration(matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+}
+
+void SearchFilterByModule::GetDescription(Stream *s) {
+ s->PutCString(", module = ");
+ s->PutCString(m_module_spec.GetFilename().AsCString("<Unknown>"));
+}
+
+uint32_t SearchFilterByModule::GetFilterRequiredItems() {
+ return eSymbolContextModule;
+}
+
+void SearchFilterByModule::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP
+SearchFilterByModule::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterByModule>(*this);
+}
+
+SearchFilterSP SearchFilterByModule::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ StructuredData::Array *modules_array;
+ bool success = data_dict.GetValueForKeyAsArray(GetKey(OptionNames::ModList),
+ modules_array);
+ if (!success) {
+ error.SetErrorString("SFBM::CFSD: Could not find the module list key.");
+ return nullptr;
+ }
+
+ size_t num_modules = modules_array->GetSize();
+ if (num_modules > 1) {
+ error.SetErrorString(
+ "SFBM::CFSD: Only one modules allowed for SearchFilterByModule.");
+ return nullptr;
+ }
+
+ llvm::StringRef module;
+ success = modules_array->GetItemAtIndexAsString(0, module);
+ if (!success) {
+ error.SetErrorString("SFBM::CFSD: filter module item not a string.");
+ return nullptr;
+ }
+ FileSpec module_spec(module);
+
+ return std::make_shared<SearchFilterByModule>(target.shared_from_this(),
+ module_spec);
+}
+
+StructuredData::ObjectSP SearchFilterByModule::SerializeToStructuredData() {
+ auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ auto module_array_sp = std::make_shared<StructuredData::Array>();
+ module_array_sp->AddItem(
+ std::make_shared<StructuredData::String>(m_module_spec.GetPath()));
+ options_dict_sp->AddItem(GetKey(OptionNames::ModList), module_array_sp);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+// SearchFilterByModuleList:
+// Selects a shared library matching a given file spec
+
+SearchFilterByModuleList::SearchFilterByModuleList(
+ const lldb::TargetSP &target_sp, const FileSpecList &module_list)
+ : SearchFilter(target_sp, FilterTy::ByModules),
+ m_module_spec_list(module_list) {}
+
+SearchFilterByModuleList::SearchFilterByModuleList(
+ const lldb::TargetSP &target_sp, const FileSpecList &module_list,
+ enum FilterTy filter_ty)
+ : SearchFilter(target_sp, filter_ty), m_module_spec_list(module_list) {}
+
+SearchFilterByModuleList &SearchFilterByModuleList::
+operator=(const SearchFilterByModuleList &rhs) {
+ m_target_sp = rhs.m_target_sp;
+ m_module_spec_list = rhs.m_module_spec_list;
+ return *this;
+}
+
+SearchFilterByModuleList::~SearchFilterByModuleList() = default;
+
+bool SearchFilterByModuleList::ModulePasses(const ModuleSP &module_sp) {
+ if (m_module_spec_list.GetSize() == 0)
+ return true;
+
+ return module_sp && m_module_spec_list.FindFileIndex(
+ 0, module_sp->GetFileSpec(), false) != UINT32_MAX;
+}
+
+bool SearchFilterByModuleList::ModulePasses(const FileSpec &spec) {
+ if (m_module_spec_list.GetSize() == 0)
+ return true;
+
+ return m_module_spec_list.FindFileIndex(0, spec, true) != UINT32_MAX;
+}
+
+bool SearchFilterByModuleList::AddressPasses(Address &address) {
+ // FIXME: Not yet implemented
+ return true;
+}
+
+bool SearchFilterByModuleList::CompUnitPasses(FileSpec &fileSpec) {
+ return true;
+}
+
+bool SearchFilterByModuleList::CompUnitPasses(CompileUnit &compUnit) {
+ return true;
+}
+
+void SearchFilterByModuleList::Search(Searcher &searcher) {
+ if (!m_target_sp)
+ return;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget) {
+ SymbolContext empty_sc;
+ empty_sc.target_sp = m_target_sp;
+ searcher.SearchCallback(*this, empty_sc, nullptr);
+ }
+
+ // If the module file spec is a full path, then we can just find the one
+ // filespec that passes. Otherwise, we need to go through all modules and
+ // find the ones that match the file name.
+
+ const ModuleList &target_modules = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
+
+ const size_t num_modules = target_modules.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ Module *module = target_modules.GetModulePointerAtIndexUnlocked(i);
+ if (m_module_spec_list.FindFileIndex(0, module->GetFileSpec(), false) !=
+ UINT32_MAX) {
+ SymbolContext matchingContext(m_target_sp, module->shared_from_this());
+ Searcher::CallbackReturn shouldContinue;
+
+ shouldContinue = DoModuleIteration(matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+}
+
+void SearchFilterByModuleList::GetDescription(Stream *s) {
+ size_t num_modules = m_module_spec_list.GetSize();
+ if (num_modules == 1) {
+ s->Printf(", module = ");
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString(
+ "<Unknown>"));
+ } else {
+ s->Printf(", modules(%" PRIu64 ") = ", (uint64_t)num_modules);
+ for (size_t i = 0; i < num_modules; i++) {
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString(
+ "<Unknown>"));
+ if (i != num_modules - 1)
+ s->PutCString(", ");
+ }
+ }
+}
+
+uint32_t SearchFilterByModuleList::GetFilterRequiredItems() {
+ return eSymbolContextModule;
+}
+
+void SearchFilterByModuleList::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP
+SearchFilterByModuleList::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterByModuleList>(*this);
+}
+
+SearchFilterSP SearchFilterByModuleList::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ StructuredData::Array *modules_array;
+ bool success = data_dict.GetValueForKeyAsArray(GetKey(OptionNames::ModList),
+ modules_array);
+ FileSpecList modules;
+ if (success) {
+ size_t num_modules = modules_array->GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ llvm::StringRef module;
+ success = modules_array->GetItemAtIndexAsString(i, module);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "SFBM::CFSD: filter module item %zu not a string.", i);
+ return nullptr;
+ }
+ modules.EmplaceBack(module);
+ }
+ }
+
+ return std::make_shared<SearchFilterByModuleList>(target.shared_from_this(),
+ modules);
+}
+
+void SearchFilterByModuleList::SerializeUnwrapped(
+ StructuredData::DictionarySP &options_dict_sp) {
+ SerializeFileSpecList(options_dict_sp, OptionNames::ModList,
+ m_module_spec_list);
+}
+
+StructuredData::ObjectSP SearchFilterByModuleList::SerializeToStructuredData() {
+ auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ SerializeUnwrapped(options_dict_sp);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+// SearchFilterByModuleListAndCU:
+// Selects a shared library matching a given file spec
+
+SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(
+ const lldb::TargetSP &target_sp, const FileSpecList &module_list,
+ const FileSpecList &cu_list)
+ : SearchFilterByModuleList(target_sp, module_list,
+ FilterTy::ByModulesAndCU),
+ m_cu_spec_list(cu_list) {}
+
+SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(
+ const SearchFilterByModuleListAndCU &rhs) = default;
+
+SearchFilterByModuleListAndCU &SearchFilterByModuleListAndCU::
+operator=(const SearchFilterByModuleListAndCU &rhs) {
+ if (&rhs != this) {
+ m_target_sp = rhs.m_target_sp;
+ m_module_spec_list = rhs.m_module_spec_list;
+ m_cu_spec_list = rhs.m_cu_spec_list;
+ }
+ return *this;
+}
+
+SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() = default;
+
+lldb::SearchFilterSP SearchFilterByModuleListAndCU::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &data_dict,
+ Status &error) {
+ StructuredData::Array *modules_array = nullptr;
+ SearchFilterSP result_sp;
+ bool success = data_dict.GetValueForKeyAsArray(GetKey(OptionNames::ModList),
+ modules_array);
+ FileSpecList modules;
+ if (success) {
+ size_t num_modules = modules_array->GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ llvm::StringRef module;
+ success = modules_array->GetItemAtIndexAsString(i, module);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "SFBM::CFSD: filter module item %zu not a string.", i);
+ return result_sp;
+ }
+ modules.EmplaceBack(module);
+ }
+ }
+
+ StructuredData::Array *cus_array = nullptr;
+ success =
+ data_dict.GetValueForKeyAsArray(GetKey(OptionNames::CUList), cus_array);
+ if (!success) {
+ error.SetErrorString("SFBM::CFSD: Could not find the CU list key.");
+ return result_sp;
+ }
+
+ size_t num_cus = cus_array->GetSize();
+ FileSpecList cus;
+ for (size_t i = 0; i < num_cus; i++) {
+ llvm::StringRef cu;
+ success = cus_array->GetItemAtIndexAsString(i, cu);
+ if (!success) {
+ error.SetErrorStringWithFormat(
+ "SFBM::CFSD: filter cu item %zu not a string.", i);
+ return nullptr;
+ }
+ cus.EmplaceBack(cu);
+ }
+
+ return std::make_shared<SearchFilterByModuleListAndCU>(
+ target.shared_from_this(), modules, cus);
+}
+
+StructuredData::ObjectSP
+SearchFilterByModuleListAndCU::SerializeToStructuredData() {
+ auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
+ SearchFilterByModuleList::SerializeUnwrapped(options_dict_sp);
+ SerializeFileSpecList(options_dict_sp, OptionNames::CUList, m_cu_spec_list);
+ return WrapOptionsDict(options_dict_sp);
+}
+
+bool SearchFilterByModuleListAndCU::AddressPasses(Address &address) {
+ SymbolContext sym_ctx;
+ address.CalculateSymbolContext(&sym_ctx, eSymbolContextEverything);
+ if (!sym_ctx.comp_unit) {
+ if (m_cu_spec_list.GetSize() != 0)
+ return false; // Has no comp_unit so can't pass the file check.
+ }
+ if (m_cu_spec_list.FindFileIndex(0, sym_ctx.comp_unit, false) == UINT32_MAX)
+ return false; // Fails the file check
+ return SearchFilterByModuleList::ModulePasses(sym_ctx.module_sp);
+}
+
+bool SearchFilterByModuleListAndCU::CompUnitPasses(FileSpec &fileSpec) {
+ return m_cu_spec_list.FindFileIndex(0, fileSpec, false) != UINT32_MAX;
+}
+
+bool SearchFilterByModuleListAndCU::CompUnitPasses(CompileUnit &compUnit) {
+ bool in_cu_list =
+ m_cu_spec_list.FindFileIndex(0, compUnit, false) != UINT32_MAX;
+ if (in_cu_list) {
+ ModuleSP module_sp(compUnit.GetModule());
+ if (module_sp) {
+ bool module_passes = SearchFilterByModuleList::ModulePasses(module_sp);
+ return module_passes;
+ } else
+ return true;
+ } else
+ return false;
+}
+
+void SearchFilterByModuleListAndCU::Search(Searcher &searcher) {
+ if (!m_target_sp)
+ return;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthTarget) {
+ SymbolContext empty_sc;
+ empty_sc.target_sp = m_target_sp;
+ searcher.SearchCallback(*this, empty_sc, nullptr);
+ }
+
+ // If the module file spec is a full path, then we can just find the one
+ // filespec that passes. Otherwise, we need to go through all modules and
+ // find the ones that match the file name.
+
+ ModuleList matching_modules;
+ const ModuleList &target_images = m_target_sp->GetImages();
+ std::lock_guard<std::recursive_mutex> guard(target_images.GetMutex());
+
+ const size_t num_modules = target_images.GetSize();
+ bool no_modules_in_filter = m_module_spec_list.GetSize() == 0;
+ for (size_t i = 0; i < num_modules; i++) {
+ lldb::ModuleSP module_sp = target_images.GetModuleAtIndexUnlocked(i);
+ if (no_modules_in_filter ||
+ m_module_spec_list.FindFileIndex(0, module_sp->GetFileSpec(), false) !=
+ UINT32_MAX) {
+ SymbolContext matchingContext(m_target_sp, module_sp);
+ Searcher::CallbackReturn shouldContinue;
+
+ if (searcher.GetDepth() == lldb::eSearchDepthModule) {
+ shouldContinue = DoModuleIteration(matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ } else {
+ const size_t num_cu = module_sp->GetNumCompileUnits();
+ for (size_t cu_idx = 0; cu_idx < num_cu; cu_idx++) {
+ CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(cu_idx);
+ matchingContext.comp_unit = cu_sp.get();
+ if (matchingContext.comp_unit) {
+ if (m_cu_spec_list.FindFileIndex(0, *matchingContext.comp_unit,
+ false) != UINT32_MAX) {
+ shouldContinue =
+ DoCUIteration(module_sp, matchingContext, searcher);
+ if (shouldContinue == Searcher::eCallbackReturnStop)
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SearchFilterByModuleListAndCU::GetDescription(Stream *s) {
+ size_t num_modules = m_module_spec_list.GetSize();
+ if (num_modules == 1) {
+ s->Printf(", module = ");
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString(
+ "<Unknown>"));
+ } else if (num_modules > 0) {
+ s->Printf(", modules(%" PRIu64 ") = ", static_cast<uint64_t>(num_modules));
+ for (size_t i = 0; i < num_modules; i++) {
+ s->PutCString(
+ m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString(
+ "<Unknown>"));
+ if (i != num_modules - 1)
+ s->PutCString(", ");
+ }
+ }
+}
+
+uint32_t SearchFilterByModuleListAndCU::GetFilterRequiredItems() {
+ return eSymbolContextModule | eSymbolContextCompUnit;
+}
+
+void SearchFilterByModuleListAndCU::Dump(Stream *s) const {}
+
+lldb::SearchFilterSP
+SearchFilterByModuleListAndCU::DoCopyForBreakpoint(Breakpoint &breakpoint) {
+ return std::make_shared<SearchFilterByModuleListAndCU>(*this);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Section.cpp b/contrib/llvm-project/lldb/source/Core/Section.cpp
new file mode 100644
index 000000000000..7615dc1d65c7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Section.cpp
@@ -0,0 +1,624 @@
+//===-- Section.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Section.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/VMRange.h"
+
+#include <inttypes.h>
+#include <limits>
+#include <utility>
+
+namespace lldb_private {
+class DataExtractor;
+}
+using namespace lldb;
+using namespace lldb_private;
+
+const char *Section::GetTypeAsCString() const {
+ switch (m_type) {
+ case eSectionTypeInvalid:
+ return "invalid";
+ case eSectionTypeCode:
+ return "code";
+ case eSectionTypeContainer:
+ return "container";
+ case eSectionTypeData:
+ return "data";
+ case eSectionTypeDataCString:
+ return "data-cstr";
+ case eSectionTypeDataCStringPointers:
+ return "data-cstr-ptr";
+ case eSectionTypeDataSymbolAddress:
+ return "data-symbol-addr";
+ case eSectionTypeData4:
+ return "data-4-byte";
+ case eSectionTypeData8:
+ return "data-8-byte";
+ case eSectionTypeData16:
+ return "data-16-byte";
+ case eSectionTypeDataPointers:
+ return "data-ptrs";
+ case eSectionTypeDebug:
+ return "debug";
+ case eSectionTypeZeroFill:
+ return "zero-fill";
+ case eSectionTypeDataObjCMessageRefs:
+ return "objc-message-refs";
+ case eSectionTypeDataObjCCFStrings:
+ return "objc-cfstrings";
+ case eSectionTypeDWARFDebugAbbrev:
+ return "dwarf-abbrev";
+ case eSectionTypeDWARFDebugAbbrevDwo:
+ return "dwarf-abbrev-dwo";
+ case eSectionTypeDWARFDebugAddr:
+ return "dwarf-addr";
+ case eSectionTypeDWARFDebugAranges:
+ return "dwarf-aranges";
+ case eSectionTypeDWARFDebugCuIndex:
+ return "dwarf-cu-index";
+ case eSectionTypeDWARFDebugFrame:
+ return "dwarf-frame";
+ case eSectionTypeDWARFDebugInfo:
+ return "dwarf-info";
+ case eSectionTypeDWARFDebugInfoDwo:
+ return "dwarf-info-dwo";
+ case eSectionTypeDWARFDebugLine:
+ return "dwarf-line";
+ case eSectionTypeDWARFDebugLineStr:
+ return "dwarf-line-str";
+ case eSectionTypeDWARFDebugLoc:
+ return "dwarf-loc";
+ case eSectionTypeDWARFDebugLocLists:
+ return "dwarf-loclists";
+ case eSectionTypeDWARFDebugMacInfo:
+ return "dwarf-macinfo";
+ case eSectionTypeDWARFDebugMacro:
+ return "dwarf-macro";
+ case eSectionTypeDWARFDebugPubNames:
+ return "dwarf-pubnames";
+ case eSectionTypeDWARFDebugPubTypes:
+ return "dwarf-pubtypes";
+ case eSectionTypeDWARFDebugRanges:
+ return "dwarf-ranges";
+ case eSectionTypeDWARFDebugRngLists:
+ return "dwarf-rnglists";
+ case eSectionTypeDWARFDebugStr:
+ return "dwarf-str";
+ case eSectionTypeDWARFDebugStrDwo:
+ return "dwarf-str-dwo";
+ case eSectionTypeDWARFDebugStrOffsets:
+ return "dwarf-str-offsets";
+ case eSectionTypeDWARFDebugStrOffsetsDwo:
+ return "dwarf-str-offsets-dwo";
+ case eSectionTypeDWARFDebugTypes:
+ return "dwarf-types";
+ case eSectionTypeDWARFDebugTypesDwo:
+ return "dwarf-types-dwo";
+ case eSectionTypeDWARFDebugNames:
+ return "dwarf-names";
+ case eSectionTypeELFSymbolTable:
+ return "elf-symbol-table";
+ case eSectionTypeELFDynamicSymbols:
+ return "elf-dynamic-symbols";
+ case eSectionTypeELFRelocationEntries:
+ return "elf-relocation-entries";
+ case eSectionTypeELFDynamicLinkInfo:
+ return "elf-dynamic-link-info";
+ case eSectionTypeDWARFAppleNames:
+ return "apple-names";
+ case eSectionTypeDWARFAppleTypes:
+ return "apple-types";
+ case eSectionTypeDWARFAppleNamespaces:
+ return "apple-namespaces";
+ case eSectionTypeDWARFAppleObjC:
+ return "apple-objc";
+ case eSectionTypeEHFrame:
+ return "eh-frame";
+ case eSectionTypeARMexidx:
+ return "ARM.exidx";
+ case eSectionTypeARMextab:
+ return "ARM.extab";
+ case eSectionTypeCompactUnwind:
+ return "compact-unwind";
+ case eSectionTypeGoSymtab:
+ return "go-symtab";
+ case eSectionTypeAbsoluteAddress:
+ return "absolute";
+ case eSectionTypeDWARFGNUDebugAltLink:
+ return "dwarf-gnu-debugaltlink";
+ case eSectionTypeOther:
+ return "regular";
+ }
+ return "unknown";
+}
+
+Section::Section(const ModuleSP &module_sp, ObjectFile *obj_file,
+ user_id_t sect_id, ConstString name,
+ SectionType sect_type, addr_t file_addr, addr_t byte_size,
+ lldb::offset_t file_offset, lldb::offset_t file_size,
+ uint32_t log2align, uint32_t flags,
+ uint32_t target_byte_size /*=1*/)
+ : ModuleChild(module_sp), UserID(sect_id), Flags(flags),
+ m_obj_file(obj_file), m_type(sect_type), m_parent_wp(), m_name(name),
+ m_file_addr(file_addr), m_byte_size(byte_size),
+ m_file_offset(file_offset), m_file_size(file_size),
+ m_log2align(log2align), m_children(), m_fake(false), m_encrypted(false),
+ m_thread_specific(false), m_readable(false), m_writable(false),
+ m_executable(false), m_relocated(false), m_target_byte_size(target_byte_size) {
+ // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ",
+ // addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 "
+ // - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s\n",
+ // this, module_sp.get(), sect_id, file_addr, file_addr +
+ // byte_size, file_offset, file_offset + file_size, flags,
+ // name.GetCString());
+}
+
+Section::Section(const lldb::SectionSP &parent_section_sp,
+ const ModuleSP &module_sp, ObjectFile *obj_file,
+ user_id_t sect_id, ConstString name,
+ SectionType sect_type, addr_t file_addr, addr_t byte_size,
+ lldb::offset_t file_offset, lldb::offset_t file_size,
+ uint32_t log2align, uint32_t flags,
+ uint32_t target_byte_size /*=1*/)
+ : ModuleChild(module_sp), UserID(sect_id), Flags(flags),
+ m_obj_file(obj_file), m_type(sect_type), m_parent_wp(), m_name(name),
+ m_file_addr(file_addr), m_byte_size(byte_size),
+ m_file_offset(file_offset), m_file_size(file_size),
+ m_log2align(log2align), m_children(), m_fake(false), m_encrypted(false),
+ m_thread_specific(false), m_readable(false), m_writable(false),
+ m_executable(false), m_relocated(false), m_target_byte_size(target_byte_size) {
+ // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ",
+ // addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 "
+ // - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s.%s\n",
+ // this, module_sp.get(), sect_id, file_addr, file_addr +
+ // byte_size, file_offset, file_offset + file_size, flags,
+ // parent_section_sp->GetName().GetCString(), name.GetCString());
+ if (parent_section_sp)
+ m_parent_wp = parent_section_sp;
+}
+
+Section::~Section() {
+ // printf ("Section::~Section(%p)\n", this);
+}
+
+addr_t Section::GetFileAddress() const {
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ // This section has a parent which means m_file_addr is an offset into the
+ // parent section, so the file address for this section is the file address
+ // of the parent plus the offset
+ return parent_sp->GetFileAddress() + m_file_addr;
+ }
+ // This section has no parent, so m_file_addr is the file base address
+ return m_file_addr;
+}
+
+bool Section::SetFileAddress(lldb::addr_t file_addr) {
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ if (m_file_addr >= file_addr)
+ return parent_sp->SetFileAddress(m_file_addr - file_addr);
+ return false;
+ } else {
+ // This section has no parent, so m_file_addr is the file base address
+ m_file_addr = file_addr;
+ return true;
+ }
+}
+
+lldb::addr_t Section::GetOffset() const {
+ // This section has a parent which means m_file_addr is an offset.
+ SectionSP parent_sp(GetParent());
+ if (parent_sp)
+ return m_file_addr;
+
+ // This section has no parent, so there is no offset to be had
+ return 0;
+}
+
+addr_t Section::GetLoadBaseAddress(Target *target) const {
+ addr_t load_base_addr = LLDB_INVALID_ADDRESS;
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ load_base_addr = parent_sp->GetLoadBaseAddress(target);
+ if (load_base_addr != LLDB_INVALID_ADDRESS)
+ load_base_addr += GetOffset();
+ }
+ if (load_base_addr == LLDB_INVALID_ADDRESS) {
+ load_base_addr = target->GetSectionLoadList().GetSectionLoadAddress(
+ const_cast<Section *>(this)->shared_from_this());
+ }
+ return load_base_addr;
+}
+
+bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr,
+ bool allow_section_end) const {
+ const size_t num_children = m_children.GetSize();
+ for (size_t i = 0; i < num_children; i++) {
+ Section *child_section = m_children.GetSectionAtIndex(i).get();
+
+ addr_t child_offset = child_section->GetOffset();
+ if (child_offset <= offset &&
+ offset - child_offset <
+ child_section->GetByteSize() + (allow_section_end ? 1 : 0))
+ return child_section->ResolveContainedAddress(offset - child_offset,
+ so_addr, allow_section_end);
+ }
+ so_addr.SetOffset(offset);
+ so_addr.SetSection(const_cast<Section *>(this)->shared_from_this());
+
+#ifdef LLDB_CONFIGURATION_DEBUG
+ // For debug builds, ensure that there are no orphaned (i.e., moduleless)
+ // sections.
+ assert(GetModule().get());
+#endif
+ return true;
+}
+
+bool Section::ContainsFileAddress(addr_t vm_addr) const {
+ const addr_t file_addr = GetFileAddress();
+ if (file_addr != LLDB_INVALID_ADDRESS && !IsThreadSpecific()) {
+ if (file_addr <= vm_addr) {
+ const addr_t offset = (vm_addr - file_addr) * m_target_byte_size;
+ return offset < GetByteSize();
+ }
+ }
+ return false;
+}
+
+int Section::Compare(const Section &a, const Section &b) {
+ if (&a == &b)
+ return 0;
+
+ const ModuleSP a_module_sp = a.GetModule();
+ const ModuleSP b_module_sp = b.GetModule();
+ if (a_module_sp == b_module_sp) {
+ user_id_t a_sect_uid = a.GetID();
+ user_id_t b_sect_uid = b.GetID();
+ if (a_sect_uid < b_sect_uid)
+ return -1;
+ if (a_sect_uid > b_sect_uid)
+ return 1;
+ return 0;
+ } else {
+ // The modules are different, just compare the module pointers
+ if (a_module_sp.get() < b_module_sp.get())
+ return -1;
+ else
+ return 1; // We already know the modules aren't equal
+ }
+}
+
+void Section::Dump(Stream *s, Target *target, uint32_t depth) const {
+ // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ s->Indent();
+ s->Printf("0x%8.8" PRIx64 " %-16s ", GetID(), GetTypeAsCString());
+ bool resolved = true;
+ addr_t addr = LLDB_INVALID_ADDRESS;
+
+ if (GetByteSize() == 0)
+ s->Printf("%39s", "");
+ else {
+ if (target)
+ addr = GetLoadBaseAddress(target);
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ if (target)
+ resolved = false;
+ addr = GetFileAddress();
+ }
+
+ VMRange range(addr, addr + m_byte_size);
+ range.Dump(s, 0);
+ }
+
+ s->Printf("%c %c%c%c 0x%8.8" PRIx64 " 0x%8.8" PRIx64 " 0x%8.8x ",
+ resolved ? ' ' : '*', m_readable ? 'r' : '-',
+ m_writable ? 'w' : '-', m_executable ? 'x' : '-', m_file_offset,
+ m_file_size, Get());
+
+ DumpName(s);
+
+ s->EOL();
+
+ if (depth > 0)
+ m_children.Dump(s, target, false, depth - 1);
+}
+
+void Section::DumpName(Stream *s) const {
+ SectionSP parent_sp(GetParent());
+ if (parent_sp) {
+ parent_sp->DumpName(s);
+ s->PutChar('.');
+ } else {
+ // The top most section prints the module basename
+ const char *name = nullptr;
+ ModuleSP module_sp(GetModule());
+
+ if (m_obj_file) {
+ const FileSpec &file_spec = m_obj_file->GetFileSpec();
+ name = file_spec.GetFilename().AsCString();
+ }
+ if ((!name || !name[0]) && module_sp)
+ name = module_sp->GetFileSpec().GetFilename().AsCString();
+ if (name && name[0])
+ s->Printf("%s.", name);
+ }
+ m_name.Dump(s);
+}
+
+bool Section::IsDescendant(const Section *section) {
+ if (this == section)
+ return true;
+ SectionSP parent_sp(GetParent());
+ if (parent_sp)
+ return parent_sp->IsDescendant(section);
+ return false;
+}
+
+bool Section::Slide(addr_t slide_amount, bool slide_children) {
+ if (m_file_addr != LLDB_INVALID_ADDRESS) {
+ if (slide_amount == 0)
+ return true;
+
+ m_file_addr += slide_amount;
+
+ if (slide_children)
+ m_children.Slide(slide_amount, slide_children);
+
+ return true;
+ }
+ return false;
+}
+
+/// Get the permissions as OR'ed bits from lldb::Permissions
+uint32_t Section::GetPermissions() const {
+ uint32_t permissions = 0;
+ if (m_readable)
+ permissions |= ePermissionsReadable;
+ if (m_writable)
+ permissions |= ePermissionsWritable;
+ if (m_executable)
+ permissions |= ePermissionsExecutable;
+ return permissions;
+}
+
+/// Set the permissions using bits OR'ed from lldb::Permissions
+void Section::SetPermissions(uint32_t permissions) {
+ m_readable = (permissions & ePermissionsReadable) != 0;
+ m_writable = (permissions & ePermissionsWritable) != 0;
+ m_executable = (permissions & ePermissionsExecutable) != 0;
+}
+
+lldb::offset_t Section::GetSectionData(void *dst, lldb::offset_t dst_len,
+ lldb::offset_t offset) {
+ if (m_obj_file)
+ return m_obj_file->ReadSectionData(this, offset, dst, dst_len);
+ return 0;
+}
+
+lldb::offset_t Section::GetSectionData(DataExtractor &section_data) {
+ if (m_obj_file)
+ return m_obj_file->ReadSectionData(this, section_data);
+ return 0;
+}
+
+#pragma mark SectionList
+
+SectionList &SectionList::operator=(const SectionList &rhs) {
+ if (this != &rhs)
+ m_sections = rhs.m_sections;
+ return *this;
+}
+
+size_t SectionList::AddSection(const lldb::SectionSP &section_sp) {
+ if (section_sp) {
+ size_t section_index = m_sections.size();
+ m_sections.push_back(section_sp);
+ return section_index;
+ }
+
+ return std::numeric_limits<size_t>::max();
+}
+
+// Warning, this can be slow as it's removing items from a std::vector.
+bool SectionList::DeleteSection(size_t idx) {
+ if (idx < m_sections.size()) {
+ m_sections.erase(m_sections.begin() + idx);
+ return true;
+ }
+ return false;
+}
+
+size_t SectionList::FindSectionIndex(const Section *sect) {
+ iterator sect_iter;
+ iterator begin = m_sections.begin();
+ iterator end = m_sections.end();
+ for (sect_iter = begin; sect_iter != end; ++sect_iter) {
+ if (sect_iter->get() == sect) {
+ // The secton was already in this section list
+ return std::distance(begin, sect_iter);
+ }
+ }
+ return UINT32_MAX;
+}
+
+size_t SectionList::AddUniqueSection(const lldb::SectionSP &sect_sp) {
+ size_t sect_idx = FindSectionIndex(sect_sp.get());
+ if (sect_idx == UINT32_MAX) {
+ sect_idx = AddSection(sect_sp);
+ }
+ return sect_idx;
+}
+
+bool SectionList::ReplaceSection(user_id_t sect_id,
+ const lldb::SectionSP &sect_sp,
+ uint32_t depth) {
+ iterator sect_iter, end = m_sections.end();
+ for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) {
+ if ((*sect_iter)->GetID() == sect_id) {
+ *sect_iter = sect_sp;
+ return true;
+ } else if (depth > 0) {
+ if ((*sect_iter)
+ ->GetChildren()
+ .ReplaceSection(sect_id, sect_sp, depth - 1))
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t SectionList::GetNumSections(uint32_t depth) const {
+ size_t count = m_sections.size();
+ if (depth > 0) {
+ const_iterator sect_iter, end = m_sections.end();
+ for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) {
+ count += (*sect_iter)->GetChildren().GetNumSections(depth - 1);
+ }
+ }
+ return count;
+}
+
+SectionSP SectionList::GetSectionAtIndex(size_t idx) const {
+ SectionSP sect_sp;
+ if (idx < m_sections.size())
+ sect_sp = m_sections[idx];
+ return sect_sp;
+}
+
+SectionSP
+SectionList::FindSectionByName(ConstString section_dstr) const {
+ SectionSP sect_sp;
+ // Check if we have a valid section string
+ if (section_dstr && !m_sections.empty()) {
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin();
+ sect_iter != end && sect_sp.get() == nullptr; ++sect_iter) {
+ Section *child_section = sect_iter->get();
+ if (child_section) {
+ if (child_section->GetName() == section_dstr) {
+ sect_sp = *sect_iter;
+ } else {
+ sect_sp =
+ child_section->GetChildren().FindSectionByName(section_dstr);
+ }
+ }
+ }
+ }
+ return sect_sp;
+}
+
+SectionSP SectionList::FindSectionByID(user_id_t sect_id) const {
+ SectionSP sect_sp;
+ if (sect_id) {
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin();
+ sect_iter != end && sect_sp.get() == nullptr; ++sect_iter) {
+ if ((*sect_iter)->GetID() == sect_id) {
+ sect_sp = *sect_iter;
+ break;
+ } else {
+ sect_sp = (*sect_iter)->GetChildren().FindSectionByID(sect_id);
+ }
+ }
+ }
+ return sect_sp;
+}
+
+SectionSP SectionList::FindSectionByType(SectionType sect_type,
+ bool check_children,
+ size_t start_idx) const {
+ SectionSP sect_sp;
+ size_t num_sections = m_sections.size();
+ for (size_t idx = start_idx; idx < num_sections; ++idx) {
+ if (m_sections[idx]->GetType() == sect_type) {
+ sect_sp = m_sections[idx];
+ break;
+ } else if (check_children) {
+ sect_sp = m_sections[idx]->GetChildren().FindSectionByType(
+ sect_type, check_children, 0);
+ if (sect_sp)
+ break;
+ }
+ }
+ return sect_sp;
+}
+
+SectionSP SectionList::FindSectionContainingFileAddress(addr_t vm_addr,
+ uint32_t depth) const {
+ SectionSP sect_sp;
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin();
+ sect_iter != end && sect_sp.get() == nullptr; ++sect_iter) {
+ Section *sect = sect_iter->get();
+ if (sect->ContainsFileAddress(vm_addr)) {
+ // The file address is in this section. We need to make sure one of our
+ // child sections doesn't contain this address as well as obeying the
+ // depth limit that was passed in.
+ if (depth > 0)
+ sect_sp = sect->GetChildren().FindSectionContainingFileAddress(
+ vm_addr, depth - 1);
+
+ if (sect_sp.get() == nullptr && !sect->IsFake())
+ sect_sp = *sect_iter;
+ }
+ }
+ return sect_sp;
+}
+
+bool SectionList::ContainsSection(user_id_t sect_id) const {
+ return FindSectionByID(sect_id).get() != nullptr;
+}
+
+void SectionList::Dump(Stream *s, Target *target, bool show_header,
+ uint32_t depth) const {
+ bool target_has_loaded_sections =
+ target && !target->GetSectionLoadList().IsEmpty();
+ if (show_header && !m_sections.empty()) {
+ s->Indent();
+ s->Printf("SectID Type %s Address "
+ " Perm File Off. File Size Flags "
+ " Section Name\n",
+ target_has_loaded_sections ? "Load" : "File");
+ s->Indent();
+ s->PutCString("---------- ---------------- "
+ "--------------------------------------- ---- ---------- "
+ "---------- "
+ "---------- ----------------------------\n");
+ }
+
+ const_iterator sect_iter;
+ const_iterator end = m_sections.end();
+ for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) {
+ (*sect_iter)->Dump(s, target_has_loaded_sections ? target : nullptr, depth);
+ }
+
+ if (show_header && !m_sections.empty())
+ s->IndentLess();
+}
+
+size_t SectionList::Slide(addr_t slide_amount, bool slide_children) {
+ size_t count = 0;
+ const_iterator pos, end = m_sections.end();
+ for (pos = m_sections.begin(); pos != end; ++pos) {
+ if ((*pos)->Slide(slide_amount, slide_children))
+ ++count;
+ }
+ return count;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/SourceManager.cpp b/contrib/llvm-project/lldb/source/Core/SourceManager.cpp
new file mode 100644
index 000000000000..42741e4ba4fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/SourceManager.cpp
@@ -0,0 +1,701 @@
+//===-- SourceManager.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/SourceManager.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Core/Highlighter.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineEntry.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/PathMappingList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferLLVM.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
+
+#include "llvm/ADT/Twine.h"
+
+#include <memory>
+#include <utility>
+
+#include <assert.h>
+#include <stdio.h>
+
+namespace lldb_private {
+class ExecutionContext;
+}
+namespace lldb_private {
+class ValueObject;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static inline bool is_newline_char(char ch) { return ch == '\n' || ch == '\r'; }
+
+// SourceManager constructor
+SourceManager::SourceManager(const TargetSP &target_sp)
+ : m_last_file_sp(), m_last_line(0), m_last_count(0), m_default_set(false),
+ m_target_wp(target_sp),
+ m_debugger_wp(target_sp->GetDebugger().shared_from_this()) {}
+
+SourceManager::SourceManager(const DebuggerSP &debugger_sp)
+ : m_last_file_sp(), m_last_line(0), m_last_count(0), m_default_set(false),
+ m_target_wp(), m_debugger_wp(debugger_sp) {}
+
+// Destructor
+SourceManager::~SourceManager() {}
+
+SourceManager::FileSP SourceManager::GetFile(const FileSpec &file_spec) {
+ bool same_as_previous =
+ m_last_file_sp && m_last_file_sp->FileSpecMatches(file_spec);
+
+ DebuggerSP debugger_sp(m_debugger_wp.lock());
+ FileSP file_sp;
+ if (same_as_previous)
+ file_sp = m_last_file_sp;
+ else if (debugger_sp)
+ file_sp = debugger_sp->GetSourceFileCache().FindSourceFile(file_spec);
+
+ TargetSP target_sp(m_target_wp.lock());
+
+ // It the target source path map has been updated, get this file again so we
+ // can successfully remap the source file
+ if (target_sp && file_sp &&
+ file_sp->GetSourceMapModificationID() !=
+ target_sp->GetSourcePathMap().GetModificationID())
+ file_sp.reset();
+
+ // Update the file contents if needed if we found a file
+ if (file_sp)
+ file_sp->UpdateIfNeeded();
+
+ // If file_sp is no good or it points to a non-existent file, reset it.
+ if (!file_sp || !FileSystem::Instance().Exists(file_sp->GetFileSpec())) {
+ if (target_sp)
+ file_sp = std::make_shared<File>(file_spec, target_sp.get());
+ else
+ file_sp = std::make_shared<File>(file_spec, debugger_sp);
+
+ if (debugger_sp)
+ debugger_sp->GetSourceFileCache().AddSourceFile(file_sp);
+ }
+ return file_sp;
+}
+
+static bool should_highlight_source(DebuggerSP debugger_sp) {
+ if (!debugger_sp)
+ return false;
+
+ // We don't use ANSI stop column formatting if the debugger doesn't think it
+ // should be using color.
+ if (!debugger_sp->GetUseColor())
+ return false;
+
+ return debugger_sp->GetHighlightSource();
+}
+
+static bool should_show_stop_column_with_ansi(DebuggerSP debugger_sp) {
+ // We don't use ANSI stop column formatting if we can't lookup values from
+ // the debugger.
+ if (!debugger_sp)
+ return false;
+
+ // We don't use ANSI stop column formatting if the debugger doesn't think it
+ // should be using color.
+ if (!debugger_sp->GetUseColor())
+ return false;
+
+ // We only use ANSI stop column formatting if we're either supposed to show
+ // ANSI where available (which we know we have when we get to this point), or
+ // if we're only supposed to use ANSI.
+ const auto value = debugger_sp->GetStopShowColumn();
+ return ((value == eStopShowColumnAnsiOrCaret) ||
+ (value == eStopShowColumnAnsi));
+}
+
+static bool should_show_stop_column_with_caret(DebuggerSP debugger_sp) {
+ // We don't use text-based stop column formatting if we can't lookup values
+ // from the debugger.
+ if (!debugger_sp)
+ return false;
+
+ // If we're asked to show the first available of ANSI or caret, then we do
+ // show the caret when ANSI is not available.
+ const auto value = debugger_sp->GetStopShowColumn();
+ if ((value == eStopShowColumnAnsiOrCaret) && !debugger_sp->GetUseColor())
+ return true;
+
+ // The only other time we use caret is if we're explicitly asked to show
+ // caret.
+ return value == eStopShowColumnCaret;
+}
+
+size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile(
+ uint32_t start_line, uint32_t count, uint32_t curr_line, uint32_t column,
+ const char *current_line_cstr, Stream *s,
+ const SymbolContextList *bp_locs) {
+ if (count == 0)
+ return 0;
+
+ Stream::ByteDelta delta(*s);
+
+ if (start_line == 0) {
+ if (m_last_line != 0 && m_last_line != UINT32_MAX)
+ start_line = m_last_line + m_last_count;
+ else
+ start_line = 1;
+ }
+
+ if (!m_default_set) {
+ FileSpec tmp_spec;
+ uint32_t tmp_line;
+ GetDefaultFileAndLine(tmp_spec, tmp_line);
+ }
+
+ m_last_line = start_line;
+ m_last_count = count;
+
+ if (m_last_file_sp.get()) {
+ const uint32_t end_line = start_line + count - 1;
+ for (uint32_t line = start_line; line <= end_line; ++line) {
+ if (!m_last_file_sp->LineIsValid(line)) {
+ m_last_line = UINT32_MAX;
+ break;
+ }
+
+ char prefix[32] = "";
+ if (bp_locs) {
+ uint32_t bp_count = bp_locs->NumLineEntriesWithLine(line);
+
+ if (bp_count > 0)
+ ::snprintf(prefix, sizeof(prefix), "[%u] ", bp_count);
+ else
+ ::snprintf(prefix, sizeof(prefix), " ");
+ }
+
+ s->Printf("%s%2.2s %-4u\t", prefix,
+ line == curr_line ? current_line_cstr : "", line);
+
+ // So far we treated column 0 as a special 'no column value', but
+ // DisplaySourceLines starts counting columns from 0 (and no column is
+ // expressed by passing an empty optional).
+ llvm::Optional<size_t> columnToHighlight;
+ if (line == curr_line && column)
+ columnToHighlight = column - 1;
+
+ size_t this_line_size =
+ m_last_file_sp->DisplaySourceLines(line, columnToHighlight, 0, 0, s);
+ if (column != 0 && line == curr_line &&
+ should_show_stop_column_with_caret(m_debugger_wp.lock())) {
+ // Display caret cursor.
+ std::string src_line;
+ m_last_file_sp->GetLine(line, src_line);
+ s->Printf(" \t");
+ // Insert a space for every non-tab character in the source line.
+ for (size_t i = 0; i + 1 < column && i < src_line.length(); ++i)
+ s->PutChar(src_line[i] == '\t' ? '\t' : ' ');
+ // Now add the caret.
+ s->Printf("^\n");
+ }
+ if (this_line_size == 0) {
+ m_last_line = UINT32_MAX;
+ break;
+ }
+ }
+ }
+ return *delta;
+}
+
+size_t SourceManager::DisplaySourceLinesWithLineNumbers(
+ const FileSpec &file_spec, uint32_t line, uint32_t column,
+ uint32_t context_before, uint32_t context_after,
+ const char *current_line_cstr, Stream *s,
+ const SymbolContextList *bp_locs) {
+ FileSP file_sp(GetFile(file_spec));
+
+ uint32_t start_line;
+ uint32_t count = context_before + context_after + 1;
+ if (line > context_before)
+ start_line = line - context_before;
+ else
+ start_line = 1;
+
+ if (m_last_file_sp.get() != file_sp.get()) {
+ if (line == 0)
+ m_last_line = 0;
+ m_last_file_sp = file_sp;
+ }
+ return DisplaySourceLinesWithLineNumbersUsingLastFile(
+ start_line, count, line, column, current_line_cstr, s, bp_locs);
+}
+
+size_t SourceManager::DisplayMoreWithLineNumbers(
+ Stream *s, uint32_t count, bool reverse, const SymbolContextList *bp_locs) {
+ // If we get called before anybody has set a default file and line, then try
+ // to figure it out here.
+ const bool have_default_file_line = m_last_file_sp && m_last_line > 0;
+ if (!m_default_set) {
+ FileSpec tmp_spec;
+ uint32_t tmp_line;
+ GetDefaultFileAndLine(tmp_spec, tmp_line);
+ }
+
+ if (m_last_file_sp) {
+ if (m_last_line == UINT32_MAX)
+ return 0;
+
+ if (reverse && m_last_line == 1)
+ return 0;
+
+ if (count > 0)
+ m_last_count = count;
+ else if (m_last_count == 0)
+ m_last_count = 10;
+
+ if (m_last_line > 0) {
+ if (reverse) {
+ // If this is the first time we've done a reverse, then back up one
+ // more time so we end up showing the chunk before the last one we've
+ // shown:
+ if (m_last_line > m_last_count)
+ m_last_line -= m_last_count;
+ else
+ m_last_line = 1;
+ } else if (have_default_file_line)
+ m_last_line += m_last_count;
+ } else
+ m_last_line = 1;
+
+ const uint32_t column = 0;
+ return DisplaySourceLinesWithLineNumbersUsingLastFile(
+ m_last_line, m_last_count, UINT32_MAX, column, "", s, bp_locs);
+ }
+ return 0;
+}
+
+bool SourceManager::SetDefaultFileAndLine(const FileSpec &file_spec,
+ uint32_t line) {
+ FileSP old_file_sp = m_last_file_sp;
+ m_last_file_sp = GetFile(file_spec);
+
+ m_default_set = true;
+ if (m_last_file_sp) {
+ m_last_line = line;
+ return true;
+ } else {
+ m_last_file_sp = old_file_sp;
+ return false;
+ }
+}
+
+bool SourceManager::GetDefaultFileAndLine(FileSpec &file_spec, uint32_t &line) {
+ if (m_last_file_sp) {
+ file_spec = m_last_file_sp->GetFileSpec();
+ line = m_last_line;
+ return true;
+ } else if (!m_default_set) {
+ TargetSP target_sp(m_target_wp.lock());
+
+ if (target_sp) {
+ // If nobody has set the default file and line then try here. If there's
+ // no executable, then we will try again later when there is one.
+ // Otherwise, if we can't find it we won't look again, somebody will have
+ // to set it (for instance when we stop somewhere...)
+ Module *executable_ptr = target_sp->GetExecutableModulePointer();
+ if (executable_ptr) {
+ SymbolContextList sc_list;
+ ConstString main_name("main");
+ bool symbols_okay = false; // Force it to be a debug symbol.
+ bool inlines_okay = true;
+ executable_ptr->FindFunctions(main_name, nullptr,
+ lldb::eFunctionNameTypeBase, inlines_okay,
+ symbols_okay, sc_list);
+ size_t num_matches = sc_list.GetSize();
+ for (size_t idx = 0; idx < num_matches; idx++) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(idx, sc);
+ if (sc.function) {
+ lldb_private::LineEntry line_entry;
+ if (sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .CalculateSymbolContextLineEntry(line_entry)) {
+ SetDefaultFileAndLine(line_entry.file, line_entry.line);
+ file_spec = m_last_file_sp->GetFileSpec();
+ line = m_last_line;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void SourceManager::FindLinesMatchingRegex(FileSpec &file_spec,
+ RegularExpression &regex,
+ uint32_t start_line,
+ uint32_t end_line,
+ std::vector<uint32_t> &match_lines) {
+ match_lines.clear();
+ FileSP file_sp = GetFile(file_spec);
+ if (!file_sp)
+ return;
+ return file_sp->FindLinesMatchingRegex(regex, start_line, end_line,
+ match_lines);
+}
+
+SourceManager::File::File(const FileSpec &file_spec,
+ lldb::DebuggerSP debugger_sp)
+ : m_file_spec_orig(file_spec), m_file_spec(file_spec),
+ m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
+ m_debugger_wp(debugger_sp) {
+ CommonInitializer(file_spec, nullptr);
+}
+
+SourceManager::File::File(const FileSpec &file_spec, Target *target)
+ : m_file_spec_orig(file_spec), m_file_spec(file_spec),
+ m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)),
+ m_debugger_wp(target ? target->GetDebugger().shared_from_this()
+ : DebuggerSP()) {
+ CommonInitializer(file_spec, target);
+}
+
+void SourceManager::File::CommonInitializer(const FileSpec &file_spec,
+ Target *target) {
+ if (m_mod_time == llvm::sys::TimePoint<>()) {
+ if (target) {
+ m_source_map_mod_id = target->GetSourcePathMap().GetModificationID();
+
+ if (!file_spec.GetDirectory() && file_spec.GetFilename()) {
+ // If this is just a file name, lets see if we can find it in the
+ // target:
+ bool check_inlines = false;
+ SymbolContextList sc_list;
+ size_t num_matches =
+ target->GetImages().ResolveSymbolContextForFilePath(
+ file_spec.GetFilename().AsCString(), 0, check_inlines,
+ SymbolContextItem(eSymbolContextModule |
+ eSymbolContextCompUnit),
+ sc_list);
+ bool got_multiple = false;
+ if (num_matches != 0) {
+ if (num_matches > 1) {
+ SymbolContext sc;
+ FileSpec *test_cu_spec = nullptr;
+
+ for (unsigned i = 0; i < num_matches; i++) {
+ sc_list.GetContextAtIndex(i, sc);
+ if (sc.comp_unit) {
+ if (test_cu_spec) {
+ if (test_cu_spec != static_cast<FileSpec *>(sc.comp_unit))
+ got_multiple = true;
+ break;
+ } else
+ test_cu_spec = sc.comp_unit;
+ }
+ }
+ }
+ if (!got_multiple) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(0, sc);
+ m_file_spec = sc.comp_unit;
+ m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
+ }
+ }
+ }
+ // Try remapping if m_file_spec does not correspond to an existing file.
+ if (!FileSystem::Instance().Exists(m_file_spec)) {
+ FileSpec new_file_spec;
+ // Check target specific source remappings first, then fall back to
+ // modules objects can have individual path remappings that were
+ // detected when the debug info for a module was found. then
+ if (target->GetSourcePathMap().FindFile(m_file_spec, new_file_spec) ||
+ target->GetImages().FindSourceFile(m_file_spec, new_file_spec)) {
+ m_file_spec = new_file_spec;
+ m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
+ }
+ }
+ }
+ }
+
+ if (m_mod_time != llvm::sys::TimePoint<>())
+ m_data_sp = FileSystem::Instance().CreateDataBuffer(m_file_spec);
+}
+
+uint32_t SourceManager::File::GetLineOffset(uint32_t line) {
+ if (line == 0)
+ return UINT32_MAX;
+
+ if (line == 1)
+ return 0;
+
+ if (CalculateLineOffsets(line)) {
+ if (line < m_offsets.size())
+ return m_offsets[line - 1]; // yes we want "line - 1" in the index
+ }
+ return UINT32_MAX;
+}
+
+uint32_t SourceManager::File::GetNumLines() {
+ CalculateLineOffsets();
+ return m_offsets.size();
+}
+
+const char *SourceManager::File::PeekLineData(uint32_t line) {
+ if (!LineIsValid(line))
+ return nullptr;
+
+ size_t line_offset = GetLineOffset(line);
+ if (line_offset < m_data_sp->GetByteSize())
+ return (const char *)m_data_sp->GetBytes() + line_offset;
+ return nullptr;
+}
+
+uint32_t SourceManager::File::GetLineLength(uint32_t line,
+ bool include_newline_chars) {
+ if (!LineIsValid(line))
+ return false;
+
+ size_t start_offset = GetLineOffset(line);
+ size_t end_offset = GetLineOffset(line + 1);
+ if (end_offset == UINT32_MAX)
+ end_offset = m_data_sp->GetByteSize();
+
+ if (end_offset > start_offset) {
+ uint32_t length = end_offset - start_offset;
+ if (!include_newline_chars) {
+ const char *line_start =
+ (const char *)m_data_sp->GetBytes() + start_offset;
+ while (length > 0) {
+ const char last_char = line_start[length - 1];
+ if ((last_char == '\r') || (last_char == '\n'))
+ --length;
+ else
+ break;
+ }
+ }
+ return length;
+ }
+ return 0;
+}
+
+bool SourceManager::File::LineIsValid(uint32_t line) {
+ if (line == 0)
+ return false;
+
+ if (CalculateLineOffsets(line))
+ return line < m_offsets.size();
+ return false;
+}
+
+void SourceManager::File::UpdateIfNeeded() {
+ // TODO: use host API to sign up for file modifications to anything in our
+ // source cache and only update when we determine a file has been updated.
+ // For now we check each time we want to display info for the file.
+ auto curr_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
+
+ if (curr_mod_time != llvm::sys::TimePoint<>() &&
+ m_mod_time != curr_mod_time) {
+ m_mod_time = curr_mod_time;
+ m_data_sp = FileSystem::Instance().CreateDataBuffer(m_file_spec);
+ m_offsets.clear();
+ }
+}
+
+size_t SourceManager::File::DisplaySourceLines(uint32_t line,
+ llvm::Optional<size_t> column,
+ uint32_t context_before,
+ uint32_t context_after,
+ Stream *s) {
+ // Nothing to write if there's no stream.
+ if (!s)
+ return 0;
+
+ // Sanity check m_data_sp before proceeding.
+ if (!m_data_sp)
+ return 0;
+
+ size_t bytes_written = s->GetWrittenBytes();
+
+ auto debugger_sp = m_debugger_wp.lock();
+
+ HighlightStyle style;
+ // Use the default Vim style if source highlighting is enabled.
+ if (should_highlight_source(debugger_sp))
+ style = HighlightStyle::MakeVimStyle();
+
+ // If we should mark the stop column with color codes, then copy the prefix
+ // and suffix to our color style.
+ if (should_show_stop_column_with_ansi(debugger_sp))
+ style.selected.Set(debugger_sp->GetStopShowColumnAnsiPrefix(),
+ debugger_sp->GetStopShowColumnAnsiSuffix());
+
+ HighlighterManager mgr;
+ std::string path = GetFileSpec().GetPath(/*denormalize*/ false);
+ // FIXME: Find a way to get the definitive language this file was written in
+ // and pass it to the highlighter.
+ const auto &h = mgr.getHighlighterFor(lldb::eLanguageTypeUnknown, path);
+
+ const uint32_t start_line =
+ line <= context_before ? 1 : line - context_before;
+ const uint32_t start_line_offset = GetLineOffset(start_line);
+ if (start_line_offset != UINT32_MAX) {
+ const uint32_t end_line = line + context_after;
+ uint32_t end_line_offset = GetLineOffset(end_line + 1);
+ if (end_line_offset == UINT32_MAX)
+ end_line_offset = m_data_sp->GetByteSize();
+
+ assert(start_line_offset <= end_line_offset);
+ if (start_line_offset < end_line_offset) {
+ size_t count = end_line_offset - start_line_offset;
+ const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
+
+ auto ref = llvm::StringRef(reinterpret_cast<const char *>(cstr), count);
+
+ h.Highlight(style, ref, column, "", *s);
+
+ // Ensure we get an end of line character one way or another.
+ if (!is_newline_char(ref.back()))
+ s->EOL();
+ }
+ }
+ return s->GetWrittenBytes() - bytes_written;
+}
+
+void SourceManager::File::FindLinesMatchingRegex(
+ RegularExpression &regex, uint32_t start_line, uint32_t end_line,
+ std::vector<uint32_t> &match_lines) {
+ match_lines.clear();
+
+ if (!LineIsValid(start_line) ||
+ (end_line != UINT32_MAX && !LineIsValid(end_line)))
+ return;
+ if (start_line > end_line)
+ return;
+
+ for (uint32_t line_no = start_line; line_no < end_line; line_no++) {
+ std::string buffer;
+ if (!GetLine(line_no, buffer))
+ break;
+ if (regex.Execute(buffer)) {
+ match_lines.push_back(line_no);
+ }
+ }
+}
+
+bool SourceManager::File::FileSpecMatches(const FileSpec &file_spec) {
+ return FileSpec::Equal(m_file_spec, file_spec, false);
+}
+
+bool lldb_private::operator==(const SourceManager::File &lhs,
+ const SourceManager::File &rhs) {
+ if (lhs.m_file_spec != rhs.m_file_spec)
+ return false;
+ return lhs.m_mod_time == rhs.m_mod_time;
+}
+
+bool SourceManager::File::CalculateLineOffsets(uint32_t line) {
+ line =
+ UINT32_MAX; // TODO: take this line out when we support partial indexing
+ if (line == UINT32_MAX) {
+ // Already done?
+ if (!m_offsets.empty() && m_offsets[0] == UINT32_MAX)
+ return true;
+
+ if (m_offsets.empty()) {
+ if (m_data_sp.get() == nullptr)
+ return false;
+
+ const char *start = (char *)m_data_sp->GetBytes();
+ if (start) {
+ const char *end = start + m_data_sp->GetByteSize();
+
+ // Calculate all line offsets from scratch
+
+ // Push a 1 at index zero to indicate the file has been completely
+ // indexed.
+ m_offsets.push_back(UINT32_MAX);
+ const char *s;
+ for (s = start; s < end; ++s) {
+ char curr_ch = *s;
+ if (is_newline_char(curr_ch)) {
+ if (s + 1 < end) {
+ char next_ch = s[1];
+ if (is_newline_char(next_ch)) {
+ if (curr_ch != next_ch)
+ ++s;
+ }
+ }
+ m_offsets.push_back(s + 1 - start);
+ }
+ }
+ if (!m_offsets.empty()) {
+ if (m_offsets.back() < size_t(end - start))
+ m_offsets.push_back(end - start);
+ }
+ return true;
+ }
+ } else {
+ // Some lines have been populated, start where we last left off
+ assert("Not implemented yet" && false);
+ }
+
+ } else {
+ // Calculate all line offsets up to "line"
+ assert("Not implemented yet" && false);
+ }
+ return false;
+}
+
+bool SourceManager::File::GetLine(uint32_t line_no, std::string &buffer) {
+ if (!LineIsValid(line_no))
+ return false;
+
+ size_t start_offset = GetLineOffset(line_no);
+ size_t end_offset = GetLineOffset(line_no + 1);
+ if (end_offset == UINT32_MAX) {
+ end_offset = m_data_sp->GetByteSize();
+ }
+ buffer.assign((char *)m_data_sp->GetBytes() + start_offset,
+ end_offset - start_offset);
+
+ return true;
+}
+
+void SourceManager::SourceFileCache::AddSourceFile(const FileSP &file_sp) {
+ FileSpec file_spec;
+ FileCache::iterator pos = m_file_cache.find(file_spec);
+ if (pos == m_file_cache.end())
+ m_file_cache[file_spec] = file_sp;
+ else {
+ if (file_sp != pos->second)
+ m_file_cache[file_spec] = file_sp;
+ }
+}
+
+SourceManager::FileSP SourceManager::SourceFileCache::FindSourceFile(
+ const FileSpec &file_spec) const {
+ FileSP file_sp;
+ FileCache::const_iterator pos = m_file_cache.find(file_spec);
+ if (pos != m_file_cache.end())
+ file_sp = pos->second;
+ return file_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp b/contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp
new file mode 100644
index 000000000000..d283749144e1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/StreamAsynchronousIO.cpp
@@ -0,0 +1,36 @@
+//===-- StreamAsynchronousIO.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/StreamAsynchronousIO.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+StreamAsynchronousIO::StreamAsynchronousIO(Debugger &debugger, bool for_stdout)
+ : Stream(0, 4, eByteOrderBig), m_debugger(debugger), m_data(),
+ m_for_stdout(for_stdout) {}
+
+StreamAsynchronousIO::~StreamAsynchronousIO() {
+ // Flush when we destroy to make sure we display the data
+ Flush();
+}
+
+void StreamAsynchronousIO::Flush() {
+ if (!m_data.empty()) {
+ m_debugger.PrintAsync(m_data.data(), m_data.size(), m_for_stdout);
+ m_data = std::string();
+ }
+}
+
+size_t StreamAsynchronousIO::WriteImpl(const void *s, size_t length) {
+ m_data.append((const char *)s, length);
+ return length;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/StreamFile.cpp b/contrib/llvm-project/lldb/source/Core/StreamFile.cpp
new file mode 100644
index 000000000000..2ddb39659d66
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/StreamFile.cpp
@@ -0,0 +1,70 @@
+//===-- StreamFile.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/Log.h"
+
+#include <stdio.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// StreamFile constructor
+StreamFile::StreamFile() : Stream() { m_file_sp = std::make_shared<File>(); }
+
+StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order)
+ : Stream(flags, addr_size, byte_order) {
+ m_file_sp = std::make_shared<File>();
+}
+
+StreamFile::StreamFile(int fd, bool transfer_ownership) : Stream() {
+ m_file_sp =
+ std::make_shared<NativeFile>(fd, File::eOpenOptionWrite, transfer_ownership);
+}
+
+StreamFile::StreamFile(FILE *fh, bool transfer_ownership) : Stream() {
+ m_file_sp = std::make_shared<NativeFile>(fh, transfer_ownership);
+}
+
+StreamFile::StreamFile(const char *path) : Stream() {
+ auto file = FileSystem::Instance().Open(
+ FileSpec(path), File::eOpenOptionWrite | File::eOpenOptionCanCreate |
+ File::eOpenOptionCloseOnExec);
+ if (file)
+ m_file_sp = std::move(file.get());
+ else {
+ // TODO refactor this so the error gets popagated up instead of logged here.
+ LLDB_LOG_ERROR(GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), file.takeError(),
+ "Cannot open {1}: {0}", path);
+ m_file_sp = std::make_shared<File>();
+ }
+}
+
+StreamFile::StreamFile(const char *path, File::OpenOptions options,
+ uint32_t permissions)
+ : Stream() {
+ auto file = FileSystem::Instance().Open(FileSpec(path), options, permissions);
+ if (file)
+ m_file_sp = std::move(file.get());
+ else {
+ // TODO refactor this so the error gets popagated up instead of logged here.
+ LLDB_LOG_ERROR(GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), file.takeError(),
+ "Cannot open {1}: {0}", path);
+ m_file_sp = std::make_shared<File>();
+ }
+}
+
+StreamFile::~StreamFile() {}
+
+void StreamFile::Flush() { m_file_sp->Flush(); }
+
+size_t StreamFile::WriteImpl(const void *s, size_t length) {
+ m_file_sp->Write(s, length);
+ return length;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp b/contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp
new file mode 100644
index 000000000000..3a656766dcec
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/UserSettingsController.cpp
@@ -0,0 +1,113 @@
+//====-- UserSettingsController.cpp ------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/UserSettingsController.h"
+
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include <memory>
+
+namespace lldb_private {
+class CommandInterpreter;
+}
+namespace lldb_private {
+class ConstString;
+}
+namespace lldb_private {
+class ExecutionContext;
+}
+namespace lldb_private {
+class Property;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::OptionValueSP
+Properties::GetPropertyValue(const ExecutionContext *exe_ctx,
+ llvm::StringRef path, bool will_modify,
+ Status &error) const {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->GetSubValue(exe_ctx, path, will_modify, error);
+ return lldb::OptionValueSP();
+}
+
+Status Properties::SetPropertyValue(const ExecutionContext *exe_ctx,
+ VarSetOperationType op,
+ llvm::StringRef path,
+ llvm::StringRef value) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->SetSubValue(exe_ctx, op, path, value);
+ Status error;
+ error.SetErrorString("no properties");
+ return error;
+}
+
+void Properties::DumpAllPropertyValues(const ExecutionContext *exe_ctx,
+ Stream &strm, uint32_t dump_mask) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->DumpValue(exe_ctx, strm, dump_mask);
+}
+
+void Properties::DumpAllDescriptions(CommandInterpreter &interpreter,
+ Stream &strm) const {
+ strm.PutCString("Top level variables:\n\n");
+
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->DumpAllDescriptions(interpreter, strm);
+}
+
+Status Properties::DumpPropertyValue(const ExecutionContext *exe_ctx,
+ Stream &strm,
+ llvm::StringRef property_path,
+ uint32_t dump_mask) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp) {
+ return properties_sp->DumpPropertyValue(exe_ctx, strm, property_path,
+ dump_mask);
+ }
+ Status error;
+ error.SetErrorString("empty property list");
+ return error;
+}
+
+size_t
+Properties::Apropos(llvm::StringRef keyword,
+ std::vector<const Property *> &matching_properties) const {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp) {
+ properties_sp->Apropos(keyword, matching_properties);
+ }
+ return matching_properties.size();
+}
+
+lldb::OptionValuePropertiesSP
+Properties::GetSubProperty(const ExecutionContext *exe_ctx,
+ ConstString name) {
+ OptionValuePropertiesSP properties_sp(GetValueProperties());
+ if (properties_sp)
+ return properties_sp->GetSubProperty(exe_ctx, name);
+ return lldb::OptionValuePropertiesSP();
+}
+
+const char *Properties::GetExperimentalSettingsName() { return "experimental"; }
+
+bool Properties::IsSettingExperimental(llvm::StringRef setting) {
+ if (setting.empty())
+ return false;
+
+ llvm::StringRef experimental = GetExperimentalSettingsName();
+ size_t dot_pos = setting.find_first_of('.');
+ return setting.take_front(dot_pos) == experimental;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/Value.cpp b/contrib/llvm-project/lldb/source/Core/Value.cpp
new file mode 100644
index 000000000000..3124b9338b36
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/Value.cpp
@@ -0,0 +1,707 @@
+//===-- Value.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Value.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/State.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+
+#include <memory>
+#include <string>
+
+#include <inttypes.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+Value::Value()
+ : m_value(), m_vector(), m_compiler_type(), m_context(nullptr),
+ m_value_type(eValueTypeScalar), m_context_type(eContextTypeInvalid),
+ m_data_buffer() {}
+
+Value::Value(const Scalar &scalar)
+ : m_value(scalar), m_vector(), m_compiler_type(), m_context(nullptr),
+ m_value_type(eValueTypeScalar), m_context_type(eContextTypeInvalid),
+ m_data_buffer() {}
+
+Value::Value(const void *bytes, int len)
+ : m_value(), m_vector(), m_compiler_type(), m_context(nullptr),
+ m_value_type(eValueTypeHostAddress), m_context_type(eContextTypeInvalid),
+ m_data_buffer() {
+ SetBytes(bytes, len);
+}
+
+Value::Value(const Value &v)
+ : m_value(v.m_value), m_vector(v.m_vector),
+ m_compiler_type(v.m_compiler_type), m_context(v.m_context),
+ m_value_type(v.m_value_type), m_context_type(v.m_context_type),
+ m_data_buffer() {
+ const uintptr_t rhs_value =
+ (uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if ((rhs_value != 0) &&
+ (rhs_value == (uintptr_t)v.m_data_buffer.GetBytes())) {
+ m_data_buffer.CopyData(v.m_data_buffer.GetBytes(),
+ v.m_data_buffer.GetByteSize());
+
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+ }
+}
+
+Value &Value::operator=(const Value &rhs) {
+ if (this != &rhs) {
+ m_value = rhs.m_value;
+ m_vector = rhs.m_vector;
+ m_compiler_type = rhs.m_compiler_type;
+ m_context = rhs.m_context;
+ m_value_type = rhs.m_value_type;
+ m_context_type = rhs.m_context_type;
+ const uintptr_t rhs_value =
+ (uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if ((rhs_value != 0) &&
+ (rhs_value == (uintptr_t)rhs.m_data_buffer.GetBytes())) {
+ m_data_buffer.CopyData(rhs.m_data_buffer.GetBytes(),
+ rhs.m_data_buffer.GetByteSize());
+
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+ }
+ }
+ return *this;
+}
+
+void Value::SetBytes(const void *bytes, int len) {
+ m_value_type = eValueTypeHostAddress;
+ m_data_buffer.CopyData(bytes, len);
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+}
+
+void Value::AppendBytes(const void *bytes, int len) {
+ m_value_type = eValueTypeHostAddress;
+ m_data_buffer.AppendData(bytes, len);
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+}
+
+void Value::Dump(Stream *strm) {
+ m_value.GetValue(strm, true);
+ strm->Printf(", value_type = %s, context = %p, context_type = %s",
+ Value::GetValueTypeAsCString(m_value_type), m_context,
+ Value::GetContextTypeAsCString(m_context_type));
+}
+
+Value::ValueType Value::GetValueType() const { return m_value_type; }
+
+AddressType Value::GetValueAddressType() const {
+ switch (m_value_type) {
+ default:
+ case eValueTypeScalar:
+ break;
+ case eValueTypeLoadAddress:
+ return eAddressTypeLoad;
+ case eValueTypeFileAddress:
+ return eAddressTypeFile;
+ case eValueTypeHostAddress:
+ return eAddressTypeHost;
+ }
+ return eAddressTypeInvalid;
+}
+
+RegisterInfo *Value::GetRegisterInfo() const {
+ if (m_context_type == eContextTypeRegisterInfo)
+ return static_cast<RegisterInfo *>(m_context);
+ return nullptr;
+}
+
+Type *Value::GetType() {
+ if (m_context_type == eContextTypeLLDBType)
+ return static_cast<Type *>(m_context);
+ return nullptr;
+}
+
+size_t Value::AppendDataToHostBuffer(const Value &rhs) {
+ if (this == &rhs)
+ return 0;
+
+ size_t curr_size = m_data_buffer.GetByteSize();
+ Status error;
+ switch (rhs.GetValueType()) {
+ case eValueTypeScalar: {
+ const size_t scalar_size = rhs.m_value.GetByteSize();
+ if (scalar_size > 0) {
+ const size_t new_size = curr_size + scalar_size;
+ if (ResizeData(new_size) == new_size) {
+ rhs.m_value.GetAsMemoryData(m_data_buffer.GetBytes() + curr_size,
+ scalar_size, endian::InlHostByteOrder(),
+ error);
+ return scalar_size;
+ }
+ }
+ } break;
+ case eValueTypeVector: {
+ const size_t vector_size = rhs.m_vector.length;
+ if (vector_size > 0) {
+ const size_t new_size = curr_size + vector_size;
+ if (ResizeData(new_size) == new_size) {
+ ::memcpy(m_data_buffer.GetBytes() + curr_size, rhs.m_vector.bytes,
+ vector_size);
+ return vector_size;
+ }
+ }
+ } break;
+ case eValueTypeFileAddress:
+ case eValueTypeLoadAddress:
+ case eValueTypeHostAddress: {
+ const uint8_t *src = rhs.GetBuffer().GetBytes();
+ const size_t src_len = rhs.GetBuffer().GetByteSize();
+ if (src && src_len > 0) {
+ const size_t new_size = curr_size + src_len;
+ if (ResizeData(new_size) == new_size) {
+ ::memcpy(m_data_buffer.GetBytes() + curr_size, src, src_len);
+ return src_len;
+ }
+ }
+ } break;
+ }
+ return 0;
+}
+
+size_t Value::ResizeData(size_t len) {
+ m_value_type = eValueTypeHostAddress;
+ m_data_buffer.SetByteSize(len);
+ m_value = (uintptr_t)m_data_buffer.GetBytes();
+ return m_data_buffer.GetByteSize();
+}
+
+bool Value::ValueOf(ExecutionContext *exe_ctx) {
+ switch (m_context_type) {
+ case eContextTypeInvalid:
+ case eContextTypeRegisterInfo: // RegisterInfo *
+ case eContextTypeLLDBType: // Type *
+ break;
+
+ case eContextTypeVariable: // Variable *
+ ResolveValue(exe_ctx);
+ return true;
+ }
+ return false;
+}
+
+uint64_t Value::GetValueByteSize(Status *error_ptr, ExecutionContext *exe_ctx) {
+ switch (m_context_type) {
+ case eContextTypeRegisterInfo: // RegisterInfo *
+ if (GetRegisterInfo()) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return GetRegisterInfo()->byte_size;
+ }
+ break;
+
+ case eContextTypeInvalid:
+ case eContextTypeLLDBType: // Type *
+ case eContextTypeVariable: // Variable *
+ {
+ auto *scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
+ if (llvm::Optional<uint64_t> size = GetCompilerType().GetByteSize(scope)) {
+ if (error_ptr)
+ error_ptr->Clear();
+ return *size;
+ }
+ break;
+ }
+ }
+ if (error_ptr && error_ptr->Success())
+ error_ptr->SetErrorString("Unable to determine byte size.");
+ return 0;
+}
+
+const CompilerType &Value::GetCompilerType() {
+ if (!m_compiler_type.IsValid()) {
+ switch (m_context_type) {
+ case eContextTypeInvalid:
+ break;
+
+ case eContextTypeRegisterInfo:
+ break; // TODO: Eventually convert into a compiler type?
+
+ case eContextTypeLLDBType: {
+ Type *lldb_type = GetType();
+ if (lldb_type)
+ m_compiler_type = lldb_type->GetForwardCompilerType();
+ } break;
+
+ case eContextTypeVariable: {
+ Variable *variable = GetVariable();
+ if (variable) {
+ Type *variable_type = variable->GetType();
+ if (variable_type)
+ m_compiler_type = variable_type->GetForwardCompilerType();
+ }
+ } break;
+ }
+ }
+
+ return m_compiler_type;
+}
+
+void Value::SetCompilerType(const CompilerType &compiler_type) {
+ m_compiler_type = compiler_type;
+}
+
+lldb::Format Value::GetValueDefaultFormat() {
+ switch (m_context_type) {
+ case eContextTypeRegisterInfo:
+ if (GetRegisterInfo())
+ return GetRegisterInfo()->format;
+ break;
+
+ case eContextTypeInvalid:
+ case eContextTypeLLDBType:
+ case eContextTypeVariable: {
+ const CompilerType &ast_type = GetCompilerType();
+ if (ast_type.IsValid())
+ return ast_type.GetFormat();
+ } break;
+ }
+
+ // Return a good default in case we can't figure anything out
+ return eFormatHex;
+}
+
+bool Value::GetData(DataExtractor &data) {
+ switch (m_value_type) {
+ default:
+ break;
+
+ case eValueTypeScalar:
+ if (m_value.GetData(data))
+ return true;
+ break;
+
+ case eValueTypeLoadAddress:
+ case eValueTypeFileAddress:
+ case eValueTypeHostAddress:
+ if (m_data_buffer.GetByteSize()) {
+ data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(),
+ data.GetByteOrder());
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
+ Module *module) {
+ data.Clear();
+
+ Status error;
+ lldb::addr_t address = LLDB_INVALID_ADDRESS;
+ AddressType address_type = eAddressTypeFile;
+ Address file_so_addr;
+ const CompilerType &ast_type = GetCompilerType();
+ switch (m_value_type) {
+ case eValueTypeVector:
+ if (ast_type.IsValid())
+ data.SetAddressByteSize(ast_type.GetPointerByteSize());
+ else
+ data.SetAddressByteSize(sizeof(void *));
+ data.SetData(m_vector.bytes, m_vector.length, m_vector.byte_order);
+ break;
+
+ case eValueTypeScalar: {
+ data.SetByteOrder(endian::InlHostByteOrder());
+ if (ast_type.IsValid())
+ data.SetAddressByteSize(ast_type.GetPointerByteSize());
+ else
+ data.SetAddressByteSize(sizeof(void *));
+
+ uint32_t limit_byte_size = UINT32_MAX;
+
+ if (llvm::Optional<uint64_t> size = ast_type.GetByteSize(
+ exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr))
+ limit_byte_size = *size;
+
+ if (limit_byte_size <= m_value.GetByteSize()) {
+ if (m_value.GetData(data, limit_byte_size))
+ return error; // Success;
+ }
+
+ error.SetErrorStringWithFormat("extracting data from value failed");
+ break;
+ }
+ case eValueTypeLoadAddress:
+ if (exe_ctx == nullptr) {
+ error.SetErrorString("can't read load address (no execution context)");
+ } else {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process == nullptr || !process->IsAlive()) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ // Allow expressions to run and evaluate things when the target has
+ // memory sections loaded. This allows you to use "target modules
+ // load" to load your executable and any shared libraries, then
+ // execute commands where you can look at types in data sections.
+ const SectionLoadList &target_sections = target->GetSectionLoadList();
+ if (!target_sections.IsEmpty()) {
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if (target_sections.ResolveLoadAddress(address, file_so_addr)) {
+ address_type = eAddressTypeLoad;
+ data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(
+ target->GetArchitecture().GetAddressByteSize());
+ } else
+ address = LLDB_INVALID_ADDRESS;
+ }
+ } else {
+ error.SetErrorString("can't read load address (invalid process)");
+ }
+ } else {
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ address_type = eAddressTypeLoad;
+ data.SetByteOrder(
+ process->GetTarget().GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(
+ process->GetTarget().GetArchitecture().GetAddressByteSize());
+ }
+ }
+ break;
+
+ case eValueTypeFileAddress:
+ if (exe_ctx == nullptr) {
+ error.SetErrorString("can't read file address (no execution context)");
+ } else if (exe_ctx->GetTargetPtr() == nullptr) {
+ error.SetErrorString("can't read file address (invalid target)");
+ } else {
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ if (address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorString("invalid file address");
+ } else {
+ if (module == nullptr) {
+ // The only thing we can currently lock down to a module so that we
+ // can resolve a file address, is a variable.
+ Variable *variable = GetVariable();
+ if (variable) {
+ SymbolContext var_sc;
+ variable->CalculateSymbolContext(&var_sc);
+ module = var_sc.module_sp.get();
+ }
+ }
+
+ if (module) {
+ bool resolved = false;
+ ObjectFile *objfile = module->GetObjectFile();
+ if (objfile) {
+ Address so_addr(address, objfile->GetSectionList());
+ addr_t load_address =
+ so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
+ bool process_launched_and_stopped =
+ exe_ctx->GetProcessPtr()
+ ? StateIsStoppedState(exe_ctx->GetProcessPtr()->GetState(),
+ true /* must_exist */)
+ : false;
+ // Don't use the load address if the process has exited.
+ if (load_address != LLDB_INVALID_ADDRESS &&
+ process_launched_and_stopped) {
+ resolved = true;
+ address = load_address;
+ address_type = eAddressTypeLoad;
+ data.SetByteOrder(
+ exe_ctx->GetTargetRef().GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(exe_ctx->GetTargetRef()
+ .GetArchitecture()
+ .GetAddressByteSize());
+ } else {
+ if (so_addr.IsSectionOffset()) {
+ resolved = true;
+ file_so_addr = so_addr;
+ data.SetByteOrder(objfile->GetByteOrder());
+ data.SetAddressByteSize(objfile->GetAddressByteSize());
+ }
+ }
+ }
+ if (!resolved) {
+ Variable *variable = GetVariable();
+
+ if (module) {
+ if (variable)
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64
+ " for variable '%s' in %s",
+ address, variable->GetName().AsCString(""),
+ module->GetFileSpec().GetPath().c_str());
+ else
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64
+ " in %s",
+ address, module->GetFileSpec().GetPath().c_str());
+ } else {
+ if (variable)
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64
+ " for variable '%s'",
+ address, variable->GetName().AsCString(""));
+ else
+ error.SetErrorStringWithFormat(
+ "unable to resolve the module for file address 0x%" PRIx64,
+ address);
+ }
+ }
+ } else {
+ // Can't convert a file address to anything valid without more
+ // context (which Module it came from)
+ error.SetErrorString(
+ "can't read memory from file address without more context");
+ }
+ }
+ }
+ break;
+
+ case eValueTypeHostAddress:
+ address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ address_type = eAddressTypeHost;
+ if (exe_ctx) {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target) {
+ data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ break;
+ }
+ }
+ // fallback to host settings
+ data.SetByteOrder(endian::InlHostByteOrder());
+ data.SetAddressByteSize(sizeof(void *));
+ break;
+ }
+
+ // Bail if we encountered any errors
+ if (error.Fail())
+ return error;
+
+ if (address == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("invalid %s address",
+ address_type == eAddressTypeHost ? "host"
+ : "load");
+ return error;
+ }
+
+ // If we got here, we need to read the value from memory
+ size_t byte_size = GetValueByteSize(&error, exe_ctx);
+
+ // Bail if we encountered any errors getting the byte size
+ if (error.Fail())
+ return error;
+
+ // No memory to read for zero-sized types.
+ if (byte_size == 0)
+ return error;
+
+ // Make sure we have enough room within "data", and if we don't make
+ // something large enough that does
+ if (!data.ValidOffsetForDataOfSize(0, byte_size)) {
+ auto data_sp = std::make_shared<DataBufferHeap>(byte_size, '\0');
+ data.SetData(data_sp);
+ }
+
+ uint8_t *dst = const_cast<uint8_t *>(data.PeekData(0, byte_size));
+ if (dst != nullptr) {
+ if (address_type == eAddressTypeHost) {
+ // The address is an address in this process, so just copy it.
+ if (address == 0) {
+ error.SetErrorStringWithFormat(
+ "trying to read from host address of 0.");
+ return error;
+ }
+ memcpy(dst, reinterpret_cast<uint8_t *>(address), byte_size);
+ } else if ((address_type == eAddressTypeLoad) ||
+ (address_type == eAddressTypeFile)) {
+ if (file_so_addr.IsValid()) {
+ // We have a file address that we were able to translate into a section
+ // offset address so we might be able to read this from the object
+ // files if we don't have a live process. Lets always try and read from
+ // the process if we have one though since we want to read the actual
+ // value by setting "prefer_file_cache" to false.
+ const bool prefer_file_cache = false;
+ if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, prefer_file_cache,
+ dst, byte_size,
+ error) != byte_size) {
+ error.SetErrorStringWithFormat(
+ "read memory from 0x%" PRIx64 " failed", (uint64_t)address);
+ }
+ } else {
+ // The execution context might have a NULL process, but it might have a
+ // valid process in the exe_ctx->target, so use the
+ // ExecutionContext::GetProcess accessor to ensure we get the process
+ // if there is one.
+ Process *process = exe_ctx->GetProcessPtr();
+
+ if (process) {
+ const size_t bytes_read =
+ process->ReadMemory(address, dst, byte_size, error);
+ if (bytes_read != byte_size)
+ error.SetErrorStringWithFormat(
+ "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
+ (uint64_t)address, (uint32_t)bytes_read, (uint32_t)byte_size);
+ } else {
+ error.SetErrorStringWithFormat("read memory from 0x%" PRIx64
+ " failed (invalid process)",
+ (uint64_t)address);
+ }
+ }
+ } else {
+ error.SetErrorStringWithFormat("unsupported AddressType value (%i)",
+ address_type);
+ }
+ } else {
+ error.SetErrorStringWithFormat("out of memory");
+ }
+
+ return error;
+}
+
+Scalar &Value::ResolveValue(ExecutionContext *exe_ctx) {
+ const CompilerType &compiler_type = GetCompilerType();
+ if (compiler_type.IsValid()) {
+ switch (m_value_type) {
+ case eValueTypeScalar: // raw scalar value
+ break;
+
+ default:
+ case eValueTypeFileAddress:
+ case eValueTypeLoadAddress: // load address value
+ case eValueTypeHostAddress: // host address value (for memory in the process
+ // that is using liblldb)
+ {
+ DataExtractor data;
+ lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS);
+ Status error(GetValueAsData(exe_ctx, data, nullptr));
+ if (error.Success()) {
+ Scalar scalar;
+ if (compiler_type.GetValueAsScalar(data, 0, data.GetByteSize(),
+ scalar)) {
+ m_value = scalar;
+ m_value_type = eValueTypeScalar;
+ } else {
+ if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
+ m_value.Clear();
+ m_value_type = eValueTypeScalar;
+ }
+ }
+ } else {
+ if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
+ m_value.Clear();
+ m_value_type = eValueTypeScalar;
+ }
+ }
+ } break;
+ }
+ }
+ return m_value;
+}
+
+Variable *Value::GetVariable() {
+ if (m_context_type == eContextTypeVariable)
+ return static_cast<Variable *>(m_context);
+ return nullptr;
+}
+
+void Value::Clear() {
+ m_value.Clear();
+ m_vector.Clear();
+ m_compiler_type.Clear();
+ m_value_type = eValueTypeScalar;
+ m_context = nullptr;
+ m_context_type = eContextTypeInvalid;
+ m_data_buffer.Clear();
+}
+
+const char *Value::GetValueTypeAsCString(ValueType value_type) {
+ switch (value_type) {
+ case eValueTypeScalar:
+ return "scalar";
+ case eValueTypeVector:
+ return "vector";
+ case eValueTypeFileAddress:
+ return "file address";
+ case eValueTypeLoadAddress:
+ return "load address";
+ case eValueTypeHostAddress:
+ return "host address";
+ };
+ return "???";
+}
+
+const char *Value::GetContextTypeAsCString(ContextType context_type) {
+ switch (context_type) {
+ case eContextTypeInvalid:
+ return "invalid";
+ case eContextTypeRegisterInfo:
+ return "RegisterInfo *";
+ case eContextTypeLLDBType:
+ return "Type *";
+ case eContextTypeVariable:
+ return "Variable *";
+ };
+ return "???";
+}
+
+void Value::ConvertToLoadAddress(Module *module, Target *target) {
+ if (!module || !target || (GetValueType() != eValueTypeFileAddress))
+ return;
+
+ lldb::addr_t file_addr = GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (file_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ Address so_addr;
+ if (!module->ResolveFileAddress(file_addr, so_addr))
+ return;
+ lldb::addr_t load_addr = so_addr.GetLoadAddress(target);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ SetValueType(Value::eValueTypeLoadAddress);
+ GetScalar() = load_addr;
+}
+
+ValueList::ValueList(const ValueList &rhs) { m_values = rhs.m_values; }
+
+const ValueList &ValueList::operator=(const ValueList &rhs) {
+ m_values = rhs.m_values;
+ return *this;
+}
+
+void ValueList::PushValue(const Value &value) { m_values.push_back(value); }
+
+size_t ValueList::GetSize() { return m_values.size(); }
+
+Value *ValueList::GetValueAtIndex(size_t idx) {
+ if (idx < GetSize()) {
+ return &(m_values[idx]);
+ } else
+ return nullptr;
+}
+
+void ValueList::Clear() { m_values.clear(); }
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObject.cpp b/contrib/llvm-project/lldb/source/Core/ValueObject.cpp
new file mode 100644
index 000000000000..74176eeace3a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObject.cpp
@@ -0,0 +1,3370 @@
+//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObject.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObjectCast.h"
+#include "lldb/Core/ValueObjectChild.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectDynamicValue.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/ValueObjectSyntheticFilter.h"
+#include "lldb/DataFormatters/DataVisualization.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/DataFormatters/StringPrinter.h"
+#include "lldb/DataFormatters/TypeFormat.h"
+#include "lldb/DataFormatters/TypeSummary.h"
+#include "lldb/DataFormatters/TypeValidator.h"
+#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/SharingPtr.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-private-types.h"
+
+#include "llvm/Support/Compiler.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <tuple>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+namespace lldb_private {
+class SymbolContextScope;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static user_id_t g_value_obj_uid = 0;
+
+// ValueObject constructor
+ValueObject::ValueObject(ValueObject &parent)
+ : UserID(++g_value_obj_uid), // Unique identifier for every value object
+ m_parent(&parent), m_root(nullptr),
+ m_update_point(parent.GetUpdatePoint()), m_name(), m_data(), m_value(),
+ m_error(), m_value_str(), m_old_value_str(), m_location_str(),
+ m_summary_str(), m_object_desc_str(), m_validation_result(),
+ m_manager(parent.GetManager()), m_children(), m_synthetic_children(),
+ m_dynamic_value(nullptr), m_synthetic_value(nullptr),
+ m_deref_valobj(nullptr), m_format(eFormatDefault),
+ m_last_format(eFormatDefault), m_last_format_mgr_revision(0),
+ m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(),
+ m_type_validator_sp(), m_user_id_of_forced_summary(),
+ m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid),
+ m_value_checksum(),
+ m_preferred_display_language(lldb::eLanguageTypeUnknown),
+ m_language_flags(0), m_value_is_valid(false), m_value_did_change(false),
+ m_children_count_valid(false), m_old_value_valid(false),
+ m_is_deref_of_parent(false), m_is_array_item_for_pointer(false),
+ m_is_bitfield_for_scalar(false), m_is_child_at_offset(false),
+ m_is_getting_summary(false),
+ m_did_calculate_complete_objc_class_type(false),
+ m_is_synthetic_children_generated(
+ parent.m_is_synthetic_children_generated) {
+ m_manager->ManageObject(this);
+}
+
+// ValueObject constructor
+ValueObject::ValueObject(ExecutionContextScope *exe_scope,
+ AddressType child_ptr_or_ref_addr_type)
+ : UserID(++g_value_obj_uid), // Unique identifier for every value object
+ m_parent(nullptr), m_root(nullptr), m_update_point(exe_scope), m_name(),
+ m_data(), m_value(), m_error(), m_value_str(), m_old_value_str(),
+ m_location_str(), m_summary_str(), m_object_desc_str(),
+ m_validation_result(), m_manager(), m_children(), m_synthetic_children(),
+ m_dynamic_value(nullptr), m_synthetic_value(nullptr),
+ m_deref_valobj(nullptr), m_format(eFormatDefault),
+ m_last_format(eFormatDefault), m_last_format_mgr_revision(0),
+ m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(),
+ m_type_validator_sp(), m_user_id_of_forced_summary(),
+ m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type),
+ m_value_checksum(),
+ m_preferred_display_language(lldb::eLanguageTypeUnknown),
+ m_language_flags(0), m_value_is_valid(false), m_value_did_change(false),
+ m_children_count_valid(false), m_old_value_valid(false),
+ m_is_deref_of_parent(false), m_is_array_item_for_pointer(false),
+ m_is_bitfield_for_scalar(false), m_is_child_at_offset(false),
+ m_is_getting_summary(false),
+ m_did_calculate_complete_objc_class_type(false),
+ m_is_synthetic_children_generated(false) {
+ m_manager = new ValueObjectManager();
+ m_manager->ManageObject(this);
+}
+
+// Destructor
+ValueObject::~ValueObject() {}
+
+bool ValueObject::UpdateValueIfNeeded(bool update_format) {
+
+ bool did_change_formats = false;
+
+ if (update_format)
+ did_change_formats = UpdateFormatsIfNeeded();
+
+ // If this is a constant value, then our success is predicated on whether we
+ // have an error or not
+ if (GetIsConstant()) {
+ // if you are constant, things might still have changed behind your back
+ // (e.g. you are a frozen object and things have changed deeper than you
+ // cared to freeze-dry yourself) in this case, your value has not changed,
+ // but "computed" entries might have, so you might now have a different
+ // summary, or a different object description. clear these so we will
+ // recompute them
+ if (update_format && !did_change_formats)
+ ClearUserVisibleData(eClearUserVisibleDataItemsSummary |
+ eClearUserVisibleDataItemsDescription);
+ return m_error.Success();
+ }
+
+ bool first_update = IsChecksumEmpty();
+
+ if (NeedsUpdating()) {
+ m_update_point.SetUpdated();
+
+ // Save the old value using swap to avoid a string copy which also will
+ // clear our m_value_str
+ if (m_value_str.empty()) {
+ m_old_value_valid = false;
+ } else {
+ m_old_value_valid = true;
+ m_old_value_str.swap(m_value_str);
+ ClearUserVisibleData(eClearUserVisibleDataItemsValue);
+ }
+
+ ClearUserVisibleData();
+
+ if (IsInScope()) {
+ const bool value_was_valid = GetValueIsValid();
+ SetValueDidChange(false);
+
+ m_error.Clear();
+
+ // Call the pure virtual function to update the value
+
+ bool need_compare_checksums = false;
+ llvm::SmallVector<uint8_t, 16> old_checksum;
+
+ if (!first_update && CanProvideValue()) {
+ need_compare_checksums = true;
+ old_checksum.resize(m_value_checksum.size());
+ std::copy(m_value_checksum.begin(), m_value_checksum.end(),
+ old_checksum.begin());
+ }
+
+ bool success = UpdateValue();
+
+ SetValueIsValid(success);
+
+ if (success) {
+ const uint64_t max_checksum_size = 128;
+ m_data.Checksum(m_value_checksum, max_checksum_size);
+ } else {
+ need_compare_checksums = false;
+ m_value_checksum.clear();
+ }
+
+ assert(!need_compare_checksums ||
+ (!old_checksum.empty() && !m_value_checksum.empty()));
+
+ if (first_update)
+ SetValueDidChange(false);
+ else if (!m_value_did_change && !success) {
+ // The value wasn't gotten successfully, so we mark this as changed if
+ // the value used to be valid and now isn't
+ SetValueDidChange(value_was_valid);
+ } else if (need_compare_checksums) {
+ SetValueDidChange(memcmp(&old_checksum[0], &m_value_checksum[0],
+ m_value_checksum.size()));
+ }
+
+ } else {
+ m_error.SetErrorString("out of scope");
+ }
+ }
+ return m_error.Success();
+}
+
+bool ValueObject::UpdateFormatsIfNeeded() {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
+ LLDB_LOGF(log,
+ "[%s %p] checking for FormatManager revisions. ValueObject "
+ "rev: %d - Global rev: %d",
+ GetName().GetCString(), static_cast<void *>(this),
+ m_last_format_mgr_revision,
+ DataVisualization::GetCurrentRevision());
+
+ bool any_change = false;
+
+ if ((m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) {
+ m_last_format_mgr_revision = DataVisualization::GetCurrentRevision();
+ any_change = true;
+
+ SetValueFormat(DataVisualization::GetFormat(*this, eNoDynamicValues));
+ SetSummaryFormat(
+ DataVisualization::GetSummaryFormat(*this, GetDynamicValueType()));
+#ifndef LLDB_DISABLE_PYTHON
+ SetSyntheticChildren(
+ DataVisualization::GetSyntheticChildren(*this, GetDynamicValueType()));
+#endif
+ SetValidator(DataVisualization::GetValidator(*this, GetDynamicValueType()));
+ }
+
+ return any_change;
+}
+
+void ValueObject::SetNeedsUpdate() {
+ m_update_point.SetNeedsUpdate();
+ // We have to clear the value string here so ConstResult children will notice
+ // if their values are changed by hand (i.e. with SetValueAsCString).
+ ClearUserVisibleData(eClearUserVisibleDataItemsValue);
+}
+
+void ValueObject::ClearDynamicTypeInformation() {
+ m_children_count_valid = false;
+ m_did_calculate_complete_objc_class_type = false;
+ m_last_format_mgr_revision = 0;
+ m_override_type = CompilerType();
+ SetValueFormat(lldb::TypeFormatImplSP());
+ SetSummaryFormat(lldb::TypeSummaryImplSP());
+ SetSyntheticChildren(lldb::SyntheticChildrenSP());
+}
+
+CompilerType ValueObject::MaybeCalculateCompleteType() {
+ CompilerType compiler_type(GetCompilerTypeImpl());
+
+ if (m_did_calculate_complete_objc_class_type) {
+ if (m_override_type.IsValid())
+ return m_override_type;
+ else
+ return compiler_type;
+ }
+
+ m_did_calculate_complete_objc_class_type = true;
+
+ ProcessSP process_sp(
+ GetUpdatePoint().GetExecutionContextRef().GetProcessSP());
+
+ if (!process_sp)
+ return compiler_type;
+
+ if (auto *runtime =
+ process_sp->GetLanguageRuntime(GetObjectRuntimeLanguage())) {
+ if (llvm::Optional<CompilerType> complete_type =
+ runtime->GetRuntimeType(compiler_type)) {
+ m_override_type = complete_type.getValue();
+ if (m_override_type.IsValid())
+ return m_override_type;
+ }
+ }
+ return compiler_type;
+}
+
+CompilerType ValueObject::GetCompilerType() {
+ return MaybeCalculateCompleteType();
+}
+
+TypeImpl ValueObject::GetTypeImpl() { return TypeImpl(GetCompilerType()); }
+
+DataExtractor &ValueObject::GetDataExtractor() {
+ UpdateValueIfNeeded(false);
+ return m_data;
+}
+
+const Status &ValueObject::GetError() {
+ UpdateValueIfNeeded(false);
+ return m_error;
+}
+
+ConstString ValueObject::GetName() const { return m_name; }
+
+const char *ValueObject::GetLocationAsCString() {
+ return GetLocationAsCStringImpl(m_value, m_data);
+}
+
+const char *ValueObject::GetLocationAsCStringImpl(const Value &value,
+ const DataExtractor &data) {
+ if (UpdateValueIfNeeded(false)) {
+ if (m_location_str.empty()) {
+ StreamString sstr;
+
+ Value::ValueType value_type = value.GetValueType();
+
+ switch (value_type) {
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ if (value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ RegisterInfo *reg_info = value.GetRegisterInfo();
+ if (reg_info) {
+ if (reg_info->name)
+ m_location_str = reg_info->name;
+ else if (reg_info->alt_name)
+ m_location_str = reg_info->alt_name;
+ if (m_location_str.empty())
+ m_location_str = (reg_info->encoding == lldb::eEncodingVector)
+ ? "vector"
+ : "scalar";
+ }
+ }
+ if (m_location_str.empty())
+ m_location_str =
+ (value_type == Value::eValueTypeVector) ? "vector" : "scalar";
+ break;
+
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeHostAddress: {
+ uint32_t addr_nibble_size = data.GetAddressByteSize() * 2;
+ sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size,
+ value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS));
+ m_location_str = sstr.GetString();
+ } break;
+ }
+ }
+ }
+ return m_location_str.c_str();
+}
+
+Value &ValueObject::GetValue() { return m_value; }
+
+const Value &ValueObject::GetValue() const { return m_value; }
+
+bool ValueObject::ResolveValue(Scalar &scalar) {
+ if (UpdateValueIfNeeded(
+ false)) // make sure that you are up to date before returning anything
+ {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Value tmp_value(m_value);
+ scalar = tmp_value.ResolveValue(&exe_ctx);
+ if (scalar.IsValid()) {
+ const uint32_t bitfield_bit_size = GetBitfieldBitSize();
+ if (bitfield_bit_size)
+ return scalar.ExtractBitfield(bitfield_bit_size,
+ GetBitfieldBitOffset());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ValueObject::IsLogicalTrue(Status &error) {
+ if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
+ LazyBool is_logical_true = language->IsLogicalTrue(*this, error);
+ switch (is_logical_true) {
+ case eLazyBoolYes:
+ case eLazyBoolNo:
+ return (is_logical_true == true);
+ case eLazyBoolCalculate:
+ break;
+ }
+ }
+
+ Scalar scalar_value;
+
+ if (!ResolveValue(scalar_value)) {
+ error.SetErrorString("failed to get a scalar result");
+ return false;
+ }
+
+ bool ret;
+ ret = scalar_value.ULongLong(1) != 0;
+ error.Clear();
+ return ret;
+}
+
+bool ValueObject::GetValueIsValid() const { return m_value_is_valid; }
+
+void ValueObject::SetValueIsValid(bool b) { m_value_is_valid = b; }
+
+bool ValueObject::GetValueDidChange() { return m_value_did_change; }
+
+void ValueObject::SetValueDidChange(bool value_changed) {
+ m_value_did_change = value_changed;
+}
+
+ValueObjectSP ValueObject::GetChildAtIndex(size_t idx, bool can_create) {
+ ValueObjectSP child_sp;
+ // We may need to update our value if we are dynamic
+ if (IsPossibleDynamicType())
+ UpdateValueIfNeeded(false);
+ if (idx < GetNumChildren()) {
+ // Check if we have already made the child value object?
+ if (can_create && !m_children.HasChildAtIndex(idx)) {
+ // No we haven't created the child at this index, so lets have our
+ // subclass do it and cache the result for quick future access.
+ m_children.SetChildAtIndex(idx, CreateChildAtIndex(idx, false, 0));
+ }
+
+ ValueObject *child = m_children.GetChildAtIndex(idx);
+ if (child != nullptr)
+ return child->GetSP();
+ }
+ return child_sp;
+}
+
+lldb::ValueObjectSP
+ValueObject::GetChildAtIndexPath(llvm::ArrayRef<size_t> idxs,
+ size_t *index_of_error) {
+ if (idxs.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (size_t idx : idxs) {
+ root = root->GetChildAtIndex(idx, true);
+ if (!root) {
+ if (index_of_error)
+ *index_of_error = idx;
+ return root;
+ }
+ }
+ return root;
+}
+
+lldb::ValueObjectSP ValueObject::GetChildAtIndexPath(
+ llvm::ArrayRef<std::pair<size_t, bool>> idxs, size_t *index_of_error) {
+ if (idxs.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (std::pair<size_t, bool> idx : idxs) {
+ root = root->GetChildAtIndex(idx.first, idx.second);
+ if (!root) {
+ if (index_of_error)
+ *index_of_error = idx.first;
+ return root;
+ }
+ }
+ return root;
+}
+
+lldb::ValueObjectSP
+ValueObject::GetChildAtNamePath(llvm::ArrayRef<ConstString> names,
+ ConstString *name_of_error) {
+ if (names.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (ConstString name : names) {
+ root = root->GetChildMemberWithName(name, true);
+ if (!root) {
+ if (name_of_error)
+ *name_of_error = name;
+ return root;
+ }
+ }
+ return root;
+}
+
+lldb::ValueObjectSP ValueObject::GetChildAtNamePath(
+ llvm::ArrayRef<std::pair<ConstString, bool>> names,
+ ConstString *name_of_error) {
+ if (names.size() == 0)
+ return GetSP();
+ ValueObjectSP root(GetSP());
+ for (std::pair<ConstString, bool> name : names) {
+ root = root->GetChildMemberWithName(name.first, name.second);
+ if (!root) {
+ if (name_of_error)
+ *name_of_error = name.first;
+ return root;
+ }
+ }
+ return root;
+}
+
+size_t ValueObject::GetIndexOfChildWithName(ConstString name) {
+ bool omit_empty_base_classes = true;
+ return GetCompilerType().GetIndexOfChildWithName(name.GetCString(),
+ omit_empty_base_classes);
+}
+
+ValueObjectSP ValueObject::GetChildMemberWithName(ConstString name,
+ bool can_create) {
+ // when getting a child by name, it could be buried inside some base classes
+ // (which really aren't part of the expression path), so we need a vector of
+ // indexes that can get us down to the correct child
+ ValueObjectSP child_sp;
+
+ // We may need to update our value if we are dynamic
+ if (IsPossibleDynamicType())
+ UpdateValueIfNeeded(false);
+
+ std::vector<uint32_t> child_indexes;
+ bool omit_empty_base_classes = true;
+ const size_t num_child_indexes =
+ GetCompilerType().GetIndexOfChildMemberWithName(
+ name.GetCString(), omit_empty_base_classes, child_indexes);
+ if (num_child_indexes > 0) {
+ std::vector<uint32_t>::const_iterator pos = child_indexes.begin();
+ std::vector<uint32_t>::const_iterator end = child_indexes.end();
+
+ child_sp = GetChildAtIndex(*pos, can_create);
+ for (++pos; pos != end; ++pos) {
+ if (child_sp) {
+ ValueObjectSP new_child_sp(child_sp->GetChildAtIndex(*pos, can_create));
+ child_sp = new_child_sp;
+ } else {
+ child_sp.reset();
+ }
+ }
+ }
+ return child_sp;
+}
+
+size_t ValueObject::GetNumChildren(uint32_t max) {
+ UpdateValueIfNeeded();
+
+ if (max < UINT32_MAX) {
+ if (m_children_count_valid) {
+ size_t children_count = m_children.GetChildrenCount();
+ return children_count <= max ? children_count : max;
+ } else
+ return CalculateNumChildren(max);
+ }
+
+ if (!m_children_count_valid) {
+ SetNumChildren(CalculateNumChildren());
+ }
+ return m_children.GetChildrenCount();
+}
+
+bool ValueObject::MightHaveChildren() {
+ bool has_children = false;
+ const uint32_t type_info = GetTypeInfo();
+ if (type_info) {
+ if (type_info & (eTypeHasChildren | eTypeIsPointer | eTypeIsReference))
+ has_children = true;
+ } else {
+ has_children = GetNumChildren() > 0;
+ }
+ return has_children;
+}
+
+// Should only be called by ValueObject::GetNumChildren()
+void ValueObject::SetNumChildren(size_t num_children) {
+ m_children_count_valid = true;
+ m_children.SetChildrenCount(num_children);
+}
+
+void ValueObject::SetName(ConstString name) { m_name = name; }
+
+ValueObject *ValueObject::CreateChildAtIndex(size_t idx,
+ bool synthetic_array_member,
+ int32_t synthetic_index) {
+ ValueObject *valobj = nullptr;
+
+ bool omit_empty_base_classes = true;
+ bool ignore_array_bounds = synthetic_array_member;
+ std::string child_name_str;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ uint64_t language_flags = 0;
+
+ const bool transparent_pointers = !synthetic_array_member;
+ CompilerType child_compiler_type;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ child_compiler_type = GetCompilerType().GetChildCompilerTypeAtIndex(
+ &exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, this, language_flags);
+ if (child_compiler_type) {
+ if (synthetic_index)
+ child_byte_offset += child_byte_size * synthetic_index;
+
+ ConstString child_name;
+ if (!child_name_str.empty())
+ child_name.SetCString(child_name_str.c_str());
+
+ valobj = new ValueObjectChild(
+ *this, child_compiler_type, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid,
+ language_flags);
+ }
+
+ return valobj;
+}
+
+bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr,
+ std::string &destination,
+ lldb::LanguageType lang) {
+ return GetSummaryAsCString(summary_ptr, destination,
+ TypeSummaryOptions().SetLanguage(lang));
+}
+
+bool ValueObject::GetSummaryAsCString(TypeSummaryImpl *summary_ptr,
+ std::string &destination,
+ const TypeSummaryOptions &options) {
+ destination.clear();
+
+ // ideally we would like to bail out if passing NULL, but if we do so we end
+ // up not providing the summary for function pointers anymore
+ if (/*summary_ptr == NULL ||*/ m_is_getting_summary)
+ return false;
+
+ m_is_getting_summary = true;
+
+ TypeSummaryOptions actual_options(options);
+
+ if (actual_options.GetLanguage() == lldb::eLanguageTypeUnknown)
+ actual_options.SetLanguage(GetPreferredDisplayLanguage());
+
+ // this is a hot path in code and we prefer to avoid setting this string all
+ // too often also clearing out other information that we might care to see in
+ // a crash log. might be useful in very specific situations though.
+ /*Host::SetCrashDescriptionWithFormat("Trying to fetch a summary for %s %s.
+ Summary provider's description is %s",
+ GetTypeName().GetCString(),
+ GetName().GetCString(),
+ summary_ptr->GetDescription().c_str());*/
+
+ if (UpdateValueIfNeeded(false) && summary_ptr) {
+ if (HasSyntheticValue())
+ m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on
+ // the synthetic children being
+ // up-to-date (e.g. ${svar%#})
+ summary_ptr->FormatObject(this, destination, actual_options);
+ }
+ m_is_getting_summary = false;
+ return !destination.empty();
+}
+
+const char *ValueObject::GetSummaryAsCString(lldb::LanguageType lang) {
+ if (UpdateValueIfNeeded(true) && m_summary_str.empty()) {
+ TypeSummaryOptions summary_options;
+ summary_options.SetLanguage(lang);
+ GetSummaryAsCString(GetSummaryFormat().get(), m_summary_str,
+ summary_options);
+ }
+ if (m_summary_str.empty())
+ return nullptr;
+ return m_summary_str.c_str();
+}
+
+bool ValueObject::GetSummaryAsCString(std::string &destination,
+ const TypeSummaryOptions &options) {
+ return GetSummaryAsCString(GetSummaryFormat().get(), destination, options);
+}
+
+bool ValueObject::IsCStringContainer(bool check_pointer) {
+ CompilerType pointee_or_element_compiler_type;
+ const Flags type_flags(GetTypeInfo(&pointee_or_element_compiler_type));
+ bool is_char_arr_ptr(type_flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ pointee_or_element_compiler_type.IsCharType());
+ if (!is_char_arr_ptr)
+ return false;
+ if (!check_pointer)
+ return true;
+ if (type_flags.Test(eTypeIsArray))
+ return true;
+ addr_t cstr_address = LLDB_INVALID_ADDRESS;
+ AddressType cstr_address_type = eAddressTypeInvalid;
+ cstr_address = GetAddressOf(true, &cstr_address_type);
+ return (cstr_address != LLDB_INVALID_ADDRESS);
+}
+
+size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx,
+ uint32_t item_count) {
+ CompilerType pointee_or_element_compiler_type;
+ const uint32_t type_info = GetTypeInfo(&pointee_or_element_compiler_type);
+ const bool is_pointer_type = type_info & eTypeIsPointer;
+ const bool is_array_type = type_info & eTypeIsArray;
+ if (!(is_pointer_type || is_array_type))
+ return 0;
+
+ if (item_count == 0)
+ return 0;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ llvm::Optional<uint64_t> item_type_size =
+ pointee_or_element_compiler_type.GetByteSize(
+ exe_ctx.GetBestExecutionContextScope());
+ if (!item_type_size)
+ return 0;
+ const uint64_t bytes = item_count * *item_type_size;
+ const uint64_t offset = item_idx * *item_type_size;
+
+ if (item_idx == 0 && item_count == 1) // simply a deref
+ {
+ if (is_pointer_type) {
+ Status error;
+ ValueObjectSP pointee_sp = Dereference(error);
+ if (error.Fail() || pointee_sp.get() == nullptr)
+ return 0;
+ return pointee_sp->GetData(data, error);
+ } else {
+ ValueObjectSP child_sp = GetChildAtIndex(0, true);
+ if (child_sp.get() == nullptr)
+ return 0;
+ Status error;
+ return child_sp->GetData(data, error);
+ }
+ return true;
+ } else /* (items > 1) */
+ {
+ Status error;
+ lldb_private::DataBufferHeap *heap_buf_ptr = nullptr;
+ lldb::DataBufferSP data_sp(heap_buf_ptr =
+ new lldb_private::DataBufferHeap());
+
+ AddressType addr_type;
+ lldb::addr_t addr = is_pointer_type ? GetPointerValue(&addr_type)
+ : GetAddressOf(true, &addr_type);
+
+ switch (addr_type) {
+ case eAddressTypeFile: {
+ ModuleSP module_sp(GetModule());
+ if (module_sp) {
+ addr = addr + offset;
+ Address so_addr;
+ module_sp->ResolveFileAddress(addr, so_addr);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ heap_buf_ptr->SetByteSize(bytes);
+ size_t bytes_read = target->ReadMemory(
+ so_addr, false, heap_buf_ptr->GetBytes(), bytes, error);
+ if (error.Success()) {
+ data.SetData(data_sp);
+ return bytes_read;
+ }
+ }
+ }
+ } break;
+ case eAddressTypeLoad: {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ heap_buf_ptr->SetByteSize(bytes);
+ size_t bytes_read = process->ReadMemory(
+ addr + offset, heap_buf_ptr->GetBytes(), bytes, error);
+ if (error.Success() || bytes_read > 0) {
+ data.SetData(data_sp);
+ return bytes_read;
+ }
+ }
+ } break;
+ case eAddressTypeHost: {
+ auto max_bytes =
+ GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope());
+ if (max_bytes && *max_bytes > offset) {
+ size_t bytes_read = std::min<uint64_t>(*max_bytes - offset, bytes);
+ addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (addr == 0 || addr == LLDB_INVALID_ADDRESS)
+ break;
+ heap_buf_ptr->CopyData((uint8_t *)(addr + offset), bytes_read);
+ data.SetData(data_sp);
+ return bytes_read;
+ }
+ } break;
+ case eAddressTypeInvalid:
+ break;
+ }
+ }
+ return 0;
+}
+
+uint64_t ValueObject::GetData(DataExtractor &data, Status &error) {
+ UpdateValueIfNeeded(false);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ error = m_value.GetValueAsData(&exe_ctx, data, GetModule().get());
+ if (error.Fail()) {
+ if (m_data.GetByteSize()) {
+ data = m_data;
+ error.Clear();
+ return data.GetByteSize();
+ } else {
+ return 0;
+ }
+ }
+ data.SetAddressByteSize(m_data.GetAddressByteSize());
+ data.SetByteOrder(m_data.GetByteOrder());
+ return data.GetByteSize();
+}
+
+bool ValueObject::SetData(DataExtractor &data, Status &error) {
+ error.Clear();
+ // Make sure our value is up to date first so that our location and location
+ // type is valid.
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t count = 0;
+ const Encoding encoding = GetCompilerType().GetEncoding(count);
+
+ const size_t byte_size = GetByteSize();
+
+ Value::ValueType value_type = m_value.GetValueType();
+
+ switch (value_type) {
+ case Value::eValueTypeScalar: {
+ Status set_error =
+ m_value.GetScalar().SetValueFromData(data, encoding, byte_size);
+
+ if (!set_error.Success()) {
+ error.SetErrorStringWithFormat("unable to set scalar value: %s",
+ set_error.AsCString());
+ return false;
+ }
+ } break;
+ case Value::eValueTypeLoadAddress: {
+ // If it is a load address, then the scalar value is the storage location
+ // of the data, and we have to shove this value down to that load location.
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ addr_t target_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ size_t bytes_written = process->WriteMemory(
+ target_addr, data.GetDataStart(), byte_size, error);
+ if (!error.Success())
+ return false;
+ if (bytes_written != byte_size) {
+ error.SetErrorString("unable to write value to memory");
+ return false;
+ }
+ }
+ } break;
+ case Value::eValueTypeHostAddress: {
+ // If it is a host address, then we stuff the scalar as a DataBuffer into
+ // the Value's data.
+ DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0));
+ m_data.SetData(buffer_sp, 0);
+ data.CopyByteOrderedData(0, byte_size,
+ const_cast<uint8_t *>(m_data.GetDataStart()),
+ byte_size, m_data.GetByteOrder());
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+ } break;
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeVector:
+ break;
+ }
+
+ // If we have reached this point, then we have successfully changed the
+ // value.
+ SetNeedsUpdate();
+ return true;
+}
+
+static bool CopyStringDataToBufferSP(const StreamString &source,
+ lldb::DataBufferSP &destination) {
+ destination = std::make_shared<DataBufferHeap>(source.GetSize() + 1, 0);
+ memcpy(destination->GetBytes(), source.GetString().data(), source.GetSize());
+ return true;
+}
+
+std::pair<size_t, bool>
+ValueObject::ReadPointedString(lldb::DataBufferSP &buffer_sp, Status &error,
+ uint32_t max_length, bool honor_array,
+ Format item_format) {
+ bool was_capped = false;
+ StreamString s;
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+
+ if (!target) {
+ s << "<no target to read from>";
+ error.SetErrorString("no target to read from");
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {0, was_capped};
+ }
+
+ if (max_length == 0)
+ max_length = target->GetMaximumSizeOfStringSummary();
+
+ size_t bytes_read = 0;
+ size_t total_bytes_read = 0;
+
+ CompilerType compiler_type = GetCompilerType();
+ CompilerType elem_or_pointee_compiler_type;
+ const Flags type_flags(GetTypeInfo(&elem_or_pointee_compiler_type));
+ if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ elem_or_pointee_compiler_type.IsCharType()) {
+ addr_t cstr_address = LLDB_INVALID_ADDRESS;
+ AddressType cstr_address_type = eAddressTypeInvalid;
+
+ size_t cstr_len = 0;
+ bool capped_data = false;
+ const bool is_array = type_flags.Test(eTypeIsArray);
+ if (is_array) {
+ // We have an array
+ uint64_t array_size = 0;
+ if (compiler_type.IsArrayType(nullptr, &array_size, nullptr)) {
+ cstr_len = array_size;
+ if (cstr_len > max_length) {
+ capped_data = true;
+ cstr_len = max_length;
+ }
+ }
+ cstr_address = GetAddressOf(true, &cstr_address_type);
+ } else {
+ // We have a pointer
+ cstr_address = GetPointerValue(&cstr_address_type);
+ }
+
+ if (cstr_address == 0 || cstr_address == LLDB_INVALID_ADDRESS) {
+ if (cstr_address_type == eAddressTypeHost && is_array) {
+ const char *cstr = GetDataExtractor().PeekCStr(0);
+ if (cstr == nullptr) {
+ s << "<invalid address>";
+ error.SetErrorString("invalid address");
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {0, was_capped};
+ }
+ buffer_sp = std::make_shared<DataBufferHeap>(cstr_len, 0);
+ memcpy(buffer_sp->GetBytes(), cstr, cstr_len);
+ return {cstr_len, was_capped};
+ } else {
+ s << "<invalid address>";
+ error.SetErrorString("invalid address");
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {0, was_capped};
+ }
+ }
+
+ Address cstr_so_addr(cstr_address);
+ DataExtractor data;
+ if (cstr_len > 0 && honor_array) {
+ // I am using GetPointeeData() here to abstract the fact that some
+ // ValueObjects are actually frozen pointers in the host but the pointed-
+ // to data lives in the debuggee, and GetPointeeData() automatically
+ // takes care of this
+ GetPointeeData(data, 0, cstr_len);
+
+ if ((bytes_read = data.GetByteSize()) > 0) {
+ total_bytes_read = bytes_read;
+ for (size_t offset = 0; offset < bytes_read; offset++)
+ s.Printf("%c", *data.PeekData(offset, 1));
+ if (capped_data)
+ was_capped = true;
+ }
+ } else {
+ cstr_len = max_length;
+ const size_t k_max_buf_size = 64;
+
+ size_t offset = 0;
+
+ int cstr_len_displayed = -1;
+ bool capped_cstr = false;
+ // I am using GetPointeeData() here to abstract the fact that some
+ // ValueObjects are actually frozen pointers in the host but the pointed-
+ // to data lives in the debuggee, and GetPointeeData() automatically
+ // takes care of this
+ while ((bytes_read = GetPointeeData(data, offset, k_max_buf_size)) > 0) {
+ total_bytes_read += bytes_read;
+ const char *cstr = data.PeekCStr(0);
+ size_t len = strnlen(cstr, k_max_buf_size);
+ if (cstr_len_displayed < 0)
+ cstr_len_displayed = len;
+
+ if (len == 0)
+ break;
+ cstr_len_displayed += len;
+ if (len > bytes_read)
+ len = bytes_read;
+ if (len > cstr_len)
+ len = cstr_len;
+
+ for (size_t offset = 0; offset < bytes_read; offset++)
+ s.Printf("%c", *data.PeekData(offset, 1));
+
+ if (len < k_max_buf_size)
+ break;
+
+ if (len >= cstr_len) {
+ capped_cstr = true;
+ break;
+ }
+
+ cstr_len -= len;
+ offset += len;
+ }
+
+ if (cstr_len_displayed >= 0) {
+ if (capped_cstr)
+ was_capped = true;
+ }
+ }
+ } else {
+ error.SetErrorString("not a string object");
+ s << "<not a string object>";
+ }
+ CopyStringDataToBufferSP(s, buffer_sp);
+ return {total_bytes_read, was_capped};
+}
+
+std::pair<TypeValidatorResult, std::string> ValueObject::GetValidationStatus() {
+ if (!UpdateValueIfNeeded(true))
+ return {TypeValidatorResult::Success,
+ ""}; // not the validator's job to discuss update problems
+
+ if (m_validation_result.hasValue())
+ return m_validation_result.getValue();
+
+ if (!m_type_validator_sp)
+ return {TypeValidatorResult::Success, ""}; // no validator no failure
+
+ auto outcome = m_type_validator_sp->FormatObject(this);
+
+ return (m_validation_result = {outcome.m_result, outcome.m_message})
+ .getValue();
+}
+
+const char *ValueObject::GetObjectDescription() {
+ if (!UpdateValueIfNeeded(true))
+ return nullptr;
+
+ // Return cached value.
+ if (!m_object_desc_str.empty())
+ return m_object_desc_str.c_str();
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return nullptr;
+
+ // Returns the object description produced by one language runtime.
+ auto get_object_description = [&](LanguageType language) -> const char * {
+ if (LanguageRuntime *runtime = process->GetLanguageRuntime(language)) {
+ StreamString s;
+ if (runtime->GetObjectDescription(s, *this)) {
+ m_object_desc_str.append(s.GetString());
+ return m_object_desc_str.c_str();
+ }
+ }
+ return nullptr;
+ };
+
+ // Try the native language runtime first.
+ LanguageType native_language = GetObjectRuntimeLanguage();
+ if (const char *desc = get_object_description(native_language))
+ return desc;
+
+ // Try the Objective-C language runtime. This fallback is necessary
+ // for Objective-C++ and mixed Objective-C / C++ programs.
+ if (Language::LanguageIsCFamily(native_language))
+ return get_object_description(eLanguageTypeObjC);
+ return nullptr;
+}
+
+bool ValueObject::GetValueAsCString(const lldb_private::TypeFormatImpl &format,
+ std::string &destination) {
+ if (UpdateValueIfNeeded(false))
+ return format.FormatObject(this, destination);
+ else
+ return false;
+}
+
+bool ValueObject::GetValueAsCString(lldb::Format format,
+ std::string &destination) {
+ return GetValueAsCString(TypeFormatImpl_Format(format), destination);
+}
+
+const char *ValueObject::GetValueAsCString() {
+ if (UpdateValueIfNeeded(true)) {
+ lldb::TypeFormatImplSP format_sp;
+ lldb::Format my_format = GetFormat();
+ if (my_format == lldb::eFormatDefault) {
+ if (m_type_format_sp)
+ format_sp = m_type_format_sp;
+ else {
+ if (m_is_bitfield_for_scalar)
+ my_format = eFormatUnsigned;
+ else {
+ if (m_value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ const RegisterInfo *reg_info = m_value.GetRegisterInfo();
+ if (reg_info)
+ my_format = reg_info->format;
+ } else {
+ my_format = GetValue().GetCompilerType().GetFormat();
+ }
+ }
+ }
+ }
+ if (my_format != m_last_format || m_value_str.empty()) {
+ m_last_format = my_format;
+ if (!format_sp)
+ format_sp = std::make_shared<TypeFormatImpl_Format>(my_format);
+ if (GetValueAsCString(*format_sp.get(), m_value_str)) {
+ if (!m_value_did_change && m_old_value_valid) {
+ // The value was gotten successfully, so we consider the value as
+ // changed if the value string differs
+ SetValueDidChange(m_old_value_str != m_value_str);
+ }
+ }
+ }
+ }
+ if (m_value_str.empty())
+ return nullptr;
+ return m_value_str.c_str();
+}
+
+// if > 8bytes, 0 is returned. this method should mostly be used to read
+// address values out of pointers
+uint64_t ValueObject::GetValueAsUnsigned(uint64_t fail_value, bool *success) {
+ // If our byte size is zero this is an aggregate type that has children
+ if (CanProvideValue()) {
+ Scalar scalar;
+ if (ResolveValue(scalar)) {
+ if (success)
+ *success = true;
+ return scalar.ULongLong(fail_value);
+ }
+ // fallthrough, otherwise...
+ }
+
+ if (success)
+ *success = false;
+ return fail_value;
+}
+
+int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) {
+ // If our byte size is zero this is an aggregate type that has children
+ if (CanProvideValue()) {
+ Scalar scalar;
+ if (ResolveValue(scalar)) {
+ if (success)
+ *success = true;
+ return scalar.SLongLong(fail_value);
+ }
+ // fallthrough, otherwise...
+ }
+
+ if (success)
+ *success = false;
+ return fail_value;
+}
+
+// if any more "special cases" are added to
+// ValueObject::DumpPrintableRepresentation() please keep this call up to date
+// by returning true for your new special cases. We will eventually move to
+// checking this call result before trying to display special cases
+bool ValueObject::HasSpecialPrintableRepresentation(
+ ValueObjectRepresentationStyle val_obj_display, Format custom_format) {
+ Flags flags(GetTypeInfo());
+ if (flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) {
+ if (IsCStringContainer(true) &&
+ (custom_format == eFormatCString || custom_format == eFormatCharArray ||
+ custom_format == eFormatChar || custom_format == eFormatVectorOfChar))
+ return true;
+
+ if (flags.Test(eTypeIsArray)) {
+ if ((custom_format == eFormatBytes) ||
+ (custom_format == eFormatBytesWithASCII))
+ return true;
+
+ if ((custom_format == eFormatVectorOfChar) ||
+ (custom_format == eFormatVectorOfFloat32) ||
+ (custom_format == eFormatVectorOfFloat64) ||
+ (custom_format == eFormatVectorOfSInt16) ||
+ (custom_format == eFormatVectorOfSInt32) ||
+ (custom_format == eFormatVectorOfSInt64) ||
+ (custom_format == eFormatVectorOfSInt8) ||
+ (custom_format == eFormatVectorOfUInt128) ||
+ (custom_format == eFormatVectorOfUInt16) ||
+ (custom_format == eFormatVectorOfUInt32) ||
+ (custom_format == eFormatVectorOfUInt64) ||
+ (custom_format == eFormatVectorOfUInt8))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ValueObject::DumpPrintableRepresentation(
+ Stream &s, ValueObjectRepresentationStyle val_obj_display,
+ Format custom_format, PrintableRepresentationSpecialCases special,
+ bool do_dump_error) {
+
+ Flags flags(GetTypeInfo());
+
+ bool allow_special =
+ (special == ValueObject::PrintableRepresentationSpecialCases::eAllow);
+ const bool only_special = false;
+
+ if (allow_special) {
+ if (flags.AnySet(eTypeIsArray | eTypeIsPointer) &&
+ val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) {
+ // when being asked to get a printable display an array or pointer type
+ // directly, try to "do the right thing"
+
+ if (IsCStringContainer(true) &&
+ (custom_format == eFormatCString ||
+ custom_format == eFormatCharArray || custom_format == eFormatChar ||
+ custom_format ==
+ eFormatVectorOfChar)) // print char[] & char* directly
+ {
+ Status error;
+ lldb::DataBufferSP buffer_sp;
+ std::pair<size_t, bool> read_string = ReadPointedString(
+ buffer_sp, error, 0, (custom_format == eFormatVectorOfChar) ||
+ (custom_format == eFormatCharArray));
+ lldb_private::formatters::StringPrinter::
+ ReadBufferAndDumpToStreamOptions options(*this);
+ options.SetData(DataExtractor(
+ buffer_sp, lldb::eByteOrderInvalid,
+ 8)); // none of this matters for a string - pass some defaults
+ options.SetStream(&s);
+ options.SetPrefixToken(nullptr);
+ options.SetQuote('"');
+ options.SetSourceSize(buffer_sp->GetByteSize());
+ options.SetIsTruncated(read_string.second);
+ formatters::StringPrinter::ReadBufferAndDumpToStream<
+ lldb_private::formatters::StringPrinter::StringElementType::ASCII>(
+ options);
+ return !error.Fail();
+ }
+
+ if (custom_format == eFormatEnum)
+ return false;
+
+ // this only works for arrays, because I have no way to know when the
+ // pointed memory ends, and no special \0 end of data marker
+ if (flags.Test(eTypeIsArray)) {
+ if ((custom_format == eFormatBytes) ||
+ (custom_format == eFormatBytesWithASCII)) {
+ const size_t count = GetNumChildren();
+
+ s << '[';
+ for (size_t low = 0; low < count; low++) {
+
+ if (low)
+ s << ',';
+
+ ValueObjectSP child = GetChildAtIndex(low, true);
+ if (!child.get()) {
+ s << "<invalid child>";
+ continue;
+ }
+ child->DumpPrintableRepresentation(
+ s, ValueObject::eValueObjectRepresentationStyleValue,
+ custom_format);
+ }
+
+ s << ']';
+
+ return true;
+ }
+
+ if ((custom_format == eFormatVectorOfChar) ||
+ (custom_format == eFormatVectorOfFloat32) ||
+ (custom_format == eFormatVectorOfFloat64) ||
+ (custom_format == eFormatVectorOfSInt16) ||
+ (custom_format == eFormatVectorOfSInt32) ||
+ (custom_format == eFormatVectorOfSInt64) ||
+ (custom_format == eFormatVectorOfSInt8) ||
+ (custom_format == eFormatVectorOfUInt128) ||
+ (custom_format == eFormatVectorOfUInt16) ||
+ (custom_format == eFormatVectorOfUInt32) ||
+ (custom_format == eFormatVectorOfUInt64) ||
+ (custom_format == eFormatVectorOfUInt8)) // arrays of bytes, bytes
+ // with ASCII or any vector
+ // format should be printed
+ // directly
+ {
+ const size_t count = GetNumChildren();
+
+ Format format = FormatManager::GetSingleItemFormat(custom_format);
+
+ s << '[';
+ for (size_t low = 0; low < count; low++) {
+
+ if (low)
+ s << ',';
+
+ ValueObjectSP child = GetChildAtIndex(low, true);
+ if (!child.get()) {
+ s << "<invalid child>";
+ continue;
+ }
+ child->DumpPrintableRepresentation(
+ s, ValueObject::eValueObjectRepresentationStyleValue, format);
+ }
+
+ s << ']';
+
+ return true;
+ }
+ }
+
+ if ((custom_format == eFormatBoolean) ||
+ (custom_format == eFormatBinary) || (custom_format == eFormatChar) ||
+ (custom_format == eFormatCharPrintable) ||
+ (custom_format == eFormatComplexFloat) ||
+ (custom_format == eFormatDecimal) || (custom_format == eFormatHex) ||
+ (custom_format == eFormatHexUppercase) ||
+ (custom_format == eFormatFloat) || (custom_format == eFormatOctal) ||
+ (custom_format == eFormatOSType) ||
+ (custom_format == eFormatUnicode16) ||
+ (custom_format == eFormatUnicode32) ||
+ (custom_format == eFormatUnsigned) ||
+ (custom_format == eFormatPointer) ||
+ (custom_format == eFormatComplexInteger) ||
+ (custom_format == eFormatComplex) ||
+ (custom_format == eFormatDefault)) // use the [] operator
+ return false;
+ }
+ }
+
+ if (only_special)
+ return false;
+
+ bool var_success = false;
+
+ {
+ llvm::StringRef str;
+
+ // this is a local stream that we are using to ensure that the data pointed
+ // to by cstr survives long enough for us to copy it to its destination -
+ // it is necessary to have this temporary storage area for cases where our
+ // desired output is not backed by some other longer-term storage
+ StreamString strm;
+
+ if (custom_format != eFormatInvalid)
+ SetFormat(custom_format);
+
+ switch (val_obj_display) {
+ case eValueObjectRepresentationStyleValue:
+ str = GetValueAsCString();
+ break;
+
+ case eValueObjectRepresentationStyleSummary:
+ str = GetSummaryAsCString();
+ break;
+
+ case eValueObjectRepresentationStyleLanguageSpecific:
+ str = GetObjectDescription();
+ break;
+
+ case eValueObjectRepresentationStyleLocation:
+ str = GetLocationAsCString();
+ break;
+
+ case eValueObjectRepresentationStyleChildrenCount:
+ strm.Printf("%" PRIu64 "", (uint64_t)GetNumChildren());
+ str = strm.GetString();
+ break;
+
+ case eValueObjectRepresentationStyleType:
+ str = GetTypeName().GetStringRef();
+ break;
+
+ case eValueObjectRepresentationStyleName:
+ str = GetName().GetStringRef();
+ break;
+
+ case eValueObjectRepresentationStyleExpressionPath:
+ GetExpressionPath(strm, false);
+ str = strm.GetString();
+ break;
+ }
+
+ if (str.empty()) {
+ if (val_obj_display == eValueObjectRepresentationStyleValue)
+ str = GetSummaryAsCString();
+ else if (val_obj_display == eValueObjectRepresentationStyleSummary) {
+ if (!CanProvideValue()) {
+ strm.Printf("%s @ %s", GetTypeName().AsCString(),
+ GetLocationAsCString());
+ str = strm.GetString();
+ } else
+ str = GetValueAsCString();
+ }
+ }
+
+ if (!str.empty())
+ s << str;
+ else {
+ if (m_error.Fail()) {
+ if (do_dump_error)
+ s.Printf("<%s>", m_error.AsCString());
+ else
+ return false;
+ } else if (val_obj_display == eValueObjectRepresentationStyleSummary)
+ s.PutCString("<no summary available>");
+ else if (val_obj_display == eValueObjectRepresentationStyleValue)
+ s.PutCString("<no value available>");
+ else if (val_obj_display ==
+ eValueObjectRepresentationStyleLanguageSpecific)
+ s.PutCString("<not a valid Objective-C object>"); // edit this if we
+ // have other runtimes
+ // that support a
+ // description
+ else
+ s.PutCString("<no printable representation>");
+ }
+
+ // we should only return false here if we could not do *anything* even if
+ // we have an error message as output, that's a success from our callers'
+ // perspective, so return true
+ var_success = true;
+
+ if (custom_format != eFormatInvalid)
+ SetFormat(eFormatDefault);
+ }
+
+ return var_success;
+}
+
+addr_t ValueObject::GetAddressOf(bool scalar_is_load_address,
+ AddressType *address_type) {
+ // Can't take address of a bitfield
+ if (IsBitfield())
+ return LLDB_INVALID_ADDRESS;
+
+ if (!UpdateValueIfNeeded(false))
+ return LLDB_INVALID_ADDRESS;
+
+ switch (m_value.GetValueType()) {
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ if (scalar_is_load_address) {
+ if (address_type)
+ *address_type = eAddressTypeLoad;
+ return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ }
+ break;
+
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress: {
+ if (address_type)
+ *address_type = m_value.GetValueAddressType();
+ return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ } break;
+ case Value::eValueTypeHostAddress: {
+ if (address_type)
+ *address_type = m_value.GetValueAddressType();
+ return LLDB_INVALID_ADDRESS;
+ } break;
+ }
+ if (address_type)
+ *address_type = eAddressTypeInvalid;
+ return LLDB_INVALID_ADDRESS;
+}
+
+addr_t ValueObject::GetPointerValue(AddressType *address_type) {
+ addr_t address = LLDB_INVALID_ADDRESS;
+ if (address_type)
+ *address_type = eAddressTypeInvalid;
+
+ if (!UpdateValueIfNeeded(false))
+ return address;
+
+ switch (m_value.GetValueType()) {
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ break;
+
+ case Value::eValueTypeHostAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress: {
+ lldb::offset_t data_offset = 0;
+ address = m_data.GetPointer(&data_offset);
+ } break;
+ }
+
+ if (address_type)
+ *address_type = GetAddressTypeOfChildren();
+
+ return address;
+}
+
+bool ValueObject::SetValueFromCString(const char *value_str, Status &error) {
+ error.Clear();
+ // Make sure our value is up to date first so that our location and location
+ // type is valid.
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t count = 0;
+ const Encoding encoding = GetCompilerType().GetEncoding(count);
+
+ const size_t byte_size = GetByteSize();
+
+ Value::ValueType value_type = m_value.GetValueType();
+
+ if (value_type == Value::eValueTypeScalar) {
+ // If the value is already a scalar, then let the scalar change itself:
+ m_value.GetScalar().SetValueFromCString(value_str, encoding, byte_size);
+ } else if (byte_size <= 16) {
+ // If the value fits in a scalar, then make a new scalar and again let the
+ // scalar code do the conversion, then figure out where to put the new
+ // value.
+ Scalar new_scalar;
+ error = new_scalar.SetValueFromCString(value_str, encoding, byte_size);
+ if (error.Success()) {
+ switch (value_type) {
+ case Value::eValueTypeLoadAddress: {
+ // If it is a load address, then the scalar value is the storage
+ // location of the data, and we have to shove this value down to that
+ // load location.
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process) {
+ addr_t target_addr =
+ m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ size_t bytes_written = process->WriteScalarToMemory(
+ target_addr, new_scalar, byte_size, error);
+ if (!error.Success())
+ return false;
+ if (bytes_written != byte_size) {
+ error.SetErrorString("unable to write value to memory");
+ return false;
+ }
+ }
+ } break;
+ case Value::eValueTypeHostAddress: {
+ // If it is a host address, then we stuff the scalar as a DataBuffer
+ // into the Value's data.
+ DataExtractor new_data;
+ new_data.SetByteOrder(m_data.GetByteOrder());
+
+ DataBufferSP buffer_sp(new DataBufferHeap(byte_size, 0));
+ m_data.SetData(buffer_sp, 0);
+ bool success = new_scalar.GetData(new_data);
+ if (success) {
+ new_data.CopyByteOrderedData(
+ 0, byte_size, const_cast<uint8_t *>(m_data.GetDataStart()),
+ byte_size, m_data.GetByteOrder());
+ }
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+
+ } break;
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ break;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ // We don't support setting things bigger than a scalar at present.
+ error.SetErrorString("unable to write aggregate data type");
+ return false;
+ }
+
+ // If we have reached this point, then we have successfully changed the
+ // value.
+ SetNeedsUpdate();
+ return true;
+}
+
+bool ValueObject::GetDeclaration(Declaration &decl) {
+ decl.Clear();
+ return false;
+}
+
+ConstString ValueObject::GetTypeName() {
+ return GetCompilerType().GetConstTypeName();
+}
+
+ConstString ValueObject::GetDisplayTypeName() { return GetTypeName(); }
+
+ConstString ValueObject::GetQualifiedTypeName() {
+ return GetCompilerType().GetConstQualifiedTypeName();
+}
+
+LanguageType ValueObject::GetObjectRuntimeLanguage() {
+ return GetCompilerType().GetMinimumLanguage();
+}
+
+void ValueObject::AddSyntheticChild(ConstString key,
+ ValueObject *valobj) {
+ m_synthetic_children[key] = valobj;
+}
+
+ValueObjectSP ValueObject::GetSyntheticChild(ConstString key) const {
+ ValueObjectSP synthetic_child_sp;
+ std::map<ConstString, ValueObject *>::const_iterator pos =
+ m_synthetic_children.find(key);
+ if (pos != m_synthetic_children.end())
+ synthetic_child_sp = pos->second->GetSP();
+ return synthetic_child_sp;
+}
+
+uint32_t
+ValueObject::GetTypeInfo(CompilerType *pointee_or_element_compiler_type) {
+ return GetCompilerType().GetTypeInfo(pointee_or_element_compiler_type);
+}
+
+bool ValueObject::IsPointerType() { return GetCompilerType().IsPointerType(); }
+
+bool ValueObject::IsArrayType() {
+ return GetCompilerType().IsArrayType(nullptr, nullptr, nullptr);
+}
+
+bool ValueObject::IsScalarType() { return GetCompilerType().IsScalarType(); }
+
+bool ValueObject::IsIntegerType(bool &is_signed) {
+ return GetCompilerType().IsIntegerType(is_signed);
+}
+
+bool ValueObject::IsPointerOrReferenceType() {
+ return GetCompilerType().IsPointerOrReferenceType();
+}
+
+bool ValueObject::IsPossibleDynamicType() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process)
+ return process->IsPossibleDynamicValue(*this);
+ else
+ return GetCompilerType().IsPossibleDynamicType(nullptr, true, true);
+}
+
+bool ValueObject::IsRuntimeSupportValue() {
+ Process *process(GetProcessSP().get());
+ if (!process)
+ return false;
+
+ // We trust the the compiler did the right thing and marked runtime support
+ // values as artificial.
+ if (!GetVariable() || !GetVariable()->IsArtificial())
+ return false;
+
+ if (auto *runtime = process->GetLanguageRuntime(GetVariable()->GetLanguage()))
+ if (runtime->IsWhitelistedRuntimeValue(GetName()))
+ return false;
+
+ return true;
+}
+
+bool ValueObject::IsNilReference() {
+ if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
+ return language->IsNilReference(*this);
+ }
+ return false;
+}
+
+bool ValueObject::IsUninitializedReference() {
+ if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) {
+ return language->IsUninitializedReference(*this);
+ }
+ return false;
+}
+
+// This allows you to create an array member using and index that doesn't not
+// fall in the normal bounds of the array. Many times structure can be defined
+// as: struct Collection {
+// uint32_t item_count;
+// Item item_array[0];
+// };
+// The size of the "item_array" is 1, but many times in practice there are more
+// items in "item_array".
+
+ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index,
+ bool can_create) {
+ ValueObjectSP synthetic_child_sp;
+ if (IsPointerType() || IsArrayType()) {
+ char index_str[64];
+ snprintf(index_str, sizeof(index_str), "[%" PRIu64 "]", (uint64_t)index);
+ ConstString index_const_str(index_str);
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(index_const_str);
+ if (!synthetic_child_sp) {
+ ValueObject *synthetic_child;
+ // We haven't made a synthetic array member for INDEX yet, so lets make
+ // one and cache it for any future reference.
+ synthetic_child = CreateChildAtIndex(0, true, index);
+
+ // Cache the value if we got one back...
+ if (synthetic_child) {
+ AddSyntheticChild(index_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(ConstString(index_str));
+ synthetic_child_sp->m_is_array_item_for_pointer = true;
+ }
+ }
+ }
+ return synthetic_child_sp;
+}
+
+ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to,
+ bool can_create) {
+ ValueObjectSP synthetic_child_sp;
+ if (IsScalarType()) {
+ char index_str[64];
+ snprintf(index_str, sizeof(index_str), "[%i-%i]", from, to);
+ ConstString index_const_str(index_str);
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(index_const_str);
+ if (!synthetic_child_sp) {
+ uint32_t bit_field_size = to - from + 1;
+ uint32_t bit_field_offset = from;
+ if (GetDataExtractor().GetByteOrder() == eByteOrderBig)
+ bit_field_offset =
+ GetByteSize() * 8 - bit_field_size - bit_field_offset;
+ // We haven't made a synthetic array member for INDEX yet, so lets make
+ // one and cache it for any future reference.
+ ValueObjectChild *synthetic_child = new ValueObjectChild(
+ *this, GetCompilerType(), index_const_str, GetByteSize(), 0,
+ bit_field_size, bit_field_offset, false, false, eAddressTypeInvalid,
+ 0);
+
+ // Cache the value if we got one back...
+ if (synthetic_child) {
+ AddSyntheticChild(index_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(ConstString(index_str));
+ synthetic_child_sp->m_is_bitfield_for_scalar = true;
+ }
+ }
+ }
+ return synthetic_child_sp;
+}
+
+ValueObjectSP ValueObject::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+
+ ValueObjectSP synthetic_child_sp;
+
+ if (name_const_str.IsEmpty()) {
+ char name_str[64];
+ snprintf(name_str, sizeof(name_str), "@%i", offset);
+ name_const_str.SetCString(name_str);
+ }
+
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(name_const_str);
+
+ if (synthetic_child_sp.get())
+ return synthetic_child_sp;
+
+ if (!can_create)
+ return {};
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ llvm::Optional<uint64_t> size =
+ type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
+ if (!size)
+ return {};
+ ValueObjectChild *synthetic_child =
+ new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
+ false, false, eAddressTypeInvalid, 0);
+ if (synthetic_child) {
+ AddSyntheticChild(name_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(name_const_str);
+ synthetic_child_sp->m_is_child_at_offset = true;
+ }
+ return synthetic_child_sp;
+}
+
+ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset,
+ const CompilerType &type,
+ bool can_create,
+ ConstString name_const_str) {
+ ValueObjectSP synthetic_child_sp;
+
+ if (name_const_str.IsEmpty()) {
+ char name_str[128];
+ snprintf(name_str, sizeof(name_str), "base%s@%i",
+ type.GetTypeName().AsCString("<unknown>"), offset);
+ name_const_str.SetCString(name_str);
+ }
+
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(name_const_str);
+
+ if (synthetic_child_sp.get())
+ return synthetic_child_sp;
+
+ if (!can_create)
+ return {};
+
+ const bool is_base_class = true;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ llvm::Optional<uint64_t> size =
+ type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
+ if (!size)
+ return {};
+ ValueObjectChild *synthetic_child =
+ new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
+ is_base_class, false, eAddressTypeInvalid, 0);
+ if (synthetic_child) {
+ AddSyntheticChild(name_const_str, synthetic_child);
+ synthetic_child_sp = synthetic_child->GetSP();
+ synthetic_child_sp->SetName(name_const_str);
+ }
+ return synthetic_child_sp;
+}
+
+// your expression path needs to have a leading . or -> (unless it somehow
+// "looks like" an array, in which case it has a leading [ symbol). while the [
+// is meaningful and should be shown to the user, . and -> are just parser
+// design, but by no means added information for the user.. strip them off
+static const char *SkipLeadingExpressionPathSeparators(const char *expression) {
+ if (!expression || !expression[0])
+ return expression;
+ if (expression[0] == '.')
+ return expression + 1;
+ if (expression[0] == '-' && expression[1] == '>')
+ return expression + 2;
+ return expression;
+}
+
+ValueObjectSP
+ValueObject::GetSyntheticExpressionPathChild(const char *expression,
+ bool can_create) {
+ ValueObjectSP synthetic_child_sp;
+ ConstString name_const_string(expression);
+ // Check if we have already created a synthetic array member in this valid
+ // object. If we have we will re-use it.
+ synthetic_child_sp = GetSyntheticChild(name_const_string);
+ if (!synthetic_child_sp) {
+ // We haven't made a synthetic array member for expression yet, so lets
+ // make one and cache it for any future reference.
+ synthetic_child_sp = GetValueForExpressionPath(
+ expression, nullptr, nullptr,
+ GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal(
+ GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None));
+
+ // Cache the value if we got one back...
+ if (synthetic_child_sp.get()) {
+ // FIXME: this causes a "real" child to end up with its name changed to
+ // the contents of expression
+ AddSyntheticChild(name_const_string, synthetic_child_sp.get());
+ synthetic_child_sp->SetName(
+ ConstString(SkipLeadingExpressionPathSeparators(expression)));
+ }
+ }
+ return synthetic_child_sp;
+}
+
+void ValueObject::CalculateSyntheticValue(bool use_synthetic) {
+ if (!use_synthetic)
+ return;
+
+ TargetSP target_sp(GetTargetSP());
+ if (target_sp && !target_sp->GetEnableSyntheticValue()) {
+ m_synthetic_value = nullptr;
+ return;
+ }
+
+ lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp);
+
+ if (!UpdateFormatsIfNeeded() && m_synthetic_value)
+ return;
+
+ if (m_synthetic_children_sp.get() == nullptr)
+ return;
+
+ if (current_synth_sp == m_synthetic_children_sp && m_synthetic_value)
+ return;
+
+ m_synthetic_value = new ValueObjectSynthetic(*this, m_synthetic_children_sp);
+}
+
+void ValueObject::CalculateDynamicValue(DynamicValueType use_dynamic) {
+ if (use_dynamic == eNoDynamicValues)
+ return;
+
+ if (!m_dynamic_value && !IsDynamic()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsPossibleDynamicValue(*this)) {
+ ClearDynamicTypeInformation();
+ m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic);
+ }
+ }
+}
+
+ValueObjectSP ValueObject::GetDynamicValue(DynamicValueType use_dynamic) {
+ if (use_dynamic == eNoDynamicValues)
+ return ValueObjectSP();
+
+ if (!IsDynamic() && m_dynamic_value == nullptr) {
+ CalculateDynamicValue(use_dynamic);
+ }
+ if (m_dynamic_value)
+ return m_dynamic_value->GetSP();
+ else
+ return ValueObjectSP();
+}
+
+ValueObjectSP ValueObject::GetStaticValue() { return GetSP(); }
+
+lldb::ValueObjectSP ValueObject::GetNonSyntheticValue() { return GetSP(); }
+
+ValueObjectSP ValueObject::GetSyntheticValue(bool use_synthetic) {
+ if (!use_synthetic)
+ return ValueObjectSP();
+
+ CalculateSyntheticValue(use_synthetic);
+
+ if (m_synthetic_value)
+ return m_synthetic_value->GetSP();
+ else
+ return ValueObjectSP();
+}
+
+bool ValueObject::HasSyntheticValue() {
+ UpdateFormatsIfNeeded();
+
+ if (m_synthetic_children_sp.get() == nullptr)
+ return false;
+
+ CalculateSyntheticValue(true);
+
+ return m_synthetic_value != nullptr;
+}
+
+bool ValueObject::GetBaseClassPath(Stream &s) {
+ if (IsBaseClass()) {
+ bool parent_had_base_class =
+ GetParent() && GetParent()->GetBaseClassPath(s);
+ CompilerType compiler_type = GetCompilerType();
+ std::string cxx_class_name;
+ bool this_had_base_class =
+ ClangASTContext::GetCXXClassName(compiler_type, cxx_class_name);
+ if (this_had_base_class) {
+ if (parent_had_base_class)
+ s.PutCString("::");
+ s.PutCString(cxx_class_name);
+ }
+ return parent_had_base_class || this_had_base_class;
+ }
+ return false;
+}
+
+ValueObject *ValueObject::GetNonBaseClassParent() {
+ if (GetParent()) {
+ if (GetParent()->IsBaseClass())
+ return GetParent()->GetNonBaseClassParent();
+ else
+ return GetParent();
+ }
+ return nullptr;
+}
+
+bool ValueObject::IsBaseClass(uint32_t &depth) {
+ if (!IsBaseClass()) {
+ depth = 0;
+ return false;
+ }
+ if (GetParent()) {
+ GetParent()->IsBaseClass(depth);
+ depth = depth + 1;
+ return true;
+ }
+ // TODO: a base of no parent? weird..
+ depth = 1;
+ return true;
+}
+
+void ValueObject::GetExpressionPath(Stream &s, bool qualify_cxx_base_classes,
+ GetExpressionPathFormat epformat) {
+ // synthetic children do not actually "exist" as part of the hierarchy, and
+ // sometimes they are consed up in ways that don't make sense from an
+ // underlying language/API standpoint. So, use a special code path here to
+ // return something that can hopefully be used in expression
+ if (m_is_synthetic_children_generated) {
+ UpdateValueIfNeeded();
+
+ if (m_value.GetValueType() == Value::eValueTypeLoadAddress) {
+ if (IsPointerOrReferenceType()) {
+ s.Printf("((%s)0x%" PRIx64 ")", GetTypeName().AsCString("void"),
+ GetValueAsUnsigned(0));
+ return;
+ } else {
+ uint64_t load_addr =
+ m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ s.Printf("(*( (%s *)0x%" PRIx64 "))", GetTypeName().AsCString("void"),
+ load_addr);
+ return;
+ }
+ }
+ }
+
+ if (CanProvideValue()) {
+ s.Printf("((%s)%s)", GetTypeName().AsCString("void"),
+ GetValueAsCString());
+ return;
+ }
+
+ return;
+ }
+
+ const bool is_deref_of_parent = IsDereferenceOfParent();
+
+ if (is_deref_of_parent &&
+ epformat == eGetExpressionPathFormatDereferencePointers) {
+ // this is the original format of GetExpressionPath() producing code like
+ // *(a_ptr).memberName, which is entirely fine, until you put this into
+ // StackFrame::GetValueForVariableExpressionPath() which prefers to see
+ // a_ptr->memberName. the eHonorPointers mode is meant to produce strings
+ // in this latter format
+ s.PutCString("*(");
+ }
+
+ ValueObject *parent = GetParent();
+
+ if (parent)
+ parent->GetExpressionPath(s, qualify_cxx_base_classes, epformat);
+
+ // if we are a deref_of_parent just because we are synthetic array members
+ // made up to allow ptr[%d] syntax to work in variable printing, then add our
+ // name ([%d]) to the expression path
+ if (m_is_array_item_for_pointer &&
+ epformat == eGetExpressionPathFormatHonorPointers)
+ s.PutCString(m_name.AsCString());
+
+ if (!IsBaseClass()) {
+ if (!is_deref_of_parent) {
+ ValueObject *non_base_class_parent = GetNonBaseClassParent();
+ if (non_base_class_parent &&
+ !non_base_class_parent->GetName().IsEmpty()) {
+ CompilerType non_base_class_parent_compiler_type =
+ non_base_class_parent->GetCompilerType();
+ if (non_base_class_parent_compiler_type) {
+ if (parent && parent->IsDereferenceOfParent() &&
+ epformat == eGetExpressionPathFormatHonorPointers) {
+ s.PutCString("->");
+ } else {
+ const uint32_t non_base_class_parent_type_info =
+ non_base_class_parent_compiler_type.GetTypeInfo();
+
+ if (non_base_class_parent_type_info & eTypeIsPointer) {
+ s.PutCString("->");
+ } else if ((non_base_class_parent_type_info & eTypeHasChildren) &&
+ !(non_base_class_parent_type_info & eTypeIsArray)) {
+ s.PutChar('.');
+ }
+ }
+ }
+ }
+
+ const char *name = GetName().GetCString();
+ if (name) {
+ if (qualify_cxx_base_classes) {
+ if (GetBaseClassPath(s))
+ s.PutCString("::");
+ }
+ s.PutCString(name);
+ }
+ }
+ }
+
+ if (is_deref_of_parent &&
+ epformat == eGetExpressionPathFormatDereferencePointers) {
+ s.PutChar(')');
+ }
+}
+
+ValueObjectSP ValueObject::GetValueForExpressionPath(
+ llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
+ ExpressionPathEndResultType *final_value_type,
+ const GetValueForExpressionPathOptions &options,
+ ExpressionPathAftermath *final_task_on_target) {
+
+ ExpressionPathScanEndReason dummy_reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnknown;
+ ExpressionPathEndResultType dummy_final_value_type =
+ ValueObject::eExpressionPathEndResultTypeInvalid;
+ ExpressionPathAftermath dummy_final_task_on_target =
+ ValueObject::eExpressionPathAftermathNothing;
+
+ ValueObjectSP ret_val = GetValueForExpressionPath_Impl(
+ expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop,
+ final_value_type ? final_value_type : &dummy_final_value_type, options,
+ final_task_on_target ? final_task_on_target
+ : &dummy_final_task_on_target);
+
+ if (!final_task_on_target ||
+ *final_task_on_target == ValueObject::eExpressionPathAftermathNothing)
+ return ret_val;
+
+ if (ret_val.get() &&
+ ((final_value_type ? *final_value_type : dummy_final_value_type) ==
+ eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress
+ // of plain objects
+ {
+ if ((final_task_on_target ? *final_task_on_target
+ : dummy_final_task_on_target) ==
+ ValueObject::eExpressionPathAftermathDereference) {
+ Status error;
+ ValueObjectSP final_value = ret_val->Dereference(error);
+ if (error.Fail() || !final_value.get()) {
+ if (reason_to_stop)
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ if (final_value_type)
+ *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ } else {
+ if (final_task_on_target)
+ *final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
+ return final_value;
+ }
+ }
+ if (*final_task_on_target ==
+ ValueObject::eExpressionPathAftermathTakeAddress) {
+ Status error;
+ ValueObjectSP final_value = ret_val->AddressOf(error);
+ if (error.Fail() || !final_value.get()) {
+ if (reason_to_stop)
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonTakingAddressFailed;
+ if (final_value_type)
+ *final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ } else {
+ if (final_task_on_target)
+ *final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
+ return final_value;
+ }
+ }
+ }
+ return ret_val; // final_task_on_target will still have its original value, so
+ // you know I did not do it
+}
+
+ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
+ llvm::StringRef expression, ExpressionPathScanEndReason *reason_to_stop,
+ ExpressionPathEndResultType *final_result,
+ const GetValueForExpressionPathOptions &options,
+ ExpressionPathAftermath *what_next) {
+ ValueObjectSP root = GetSP();
+
+ if (!root)
+ return nullptr;
+
+ llvm::StringRef remainder = expression;
+
+ while (true) {
+ llvm::StringRef temp_expression = remainder;
+
+ CompilerType root_compiler_type = root->GetCompilerType();
+ CompilerType pointee_compiler_type;
+ Flags pointee_compiler_type_info;
+
+ Flags root_compiler_type_info(
+ root_compiler_type.GetTypeInfo(&pointee_compiler_type));
+ if (pointee_compiler_type)
+ pointee_compiler_type_info.Reset(pointee_compiler_type.GetTypeInfo());
+
+ if (temp_expression.empty()) {
+ *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString;
+ return root;
+ }
+
+ switch (temp_expression.front()) {
+ case '-': {
+ temp_expression = temp_expression.drop_front();
+ if (options.m_check_dot_vs_arrow_syntax &&
+ root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
+ // use -> on a
+ // non-pointer and I
+ // must catch the error
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ if (root_compiler_type_info.Test(eTypeIsObjC) && // if yo are trying to
+ // extract an ObjC IVar
+ // when this is forbidden
+ root_compiler_type_info.Test(eTypeIsPointer) &&
+ options.m_no_fragile_ivar) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ if (!temp_expression.startswith(">")) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ }
+ LLVM_FALLTHROUGH;
+ case '.': // or fallthrough from ->
+ {
+ if (options.m_check_dot_vs_arrow_syntax &&
+ temp_expression.front() == '.' &&
+ root_compiler_type_info.Test(eTypeIsPointer)) // if you are trying to
+ // use . on a pointer
+ // and I must catch the
+ // error
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ temp_expression = temp_expression.drop_front(); // skip . or >
+
+ size_t next_sep_pos = temp_expression.find_first_of("-.[", 1);
+ ConstString child_name;
+ if (next_sep_pos == llvm::StringRef::npos) // if no other separator just
+ // expand this last layer
+ {
+ child_name.SetString(temp_expression);
+ ValueObjectSP child_valobj_sp =
+ root->GetChildMemberWithName(child_name, true);
+
+ if (child_valobj_sp.get()) // we know we are done, so just return
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEndOfString;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ return child_valobj_sp;
+ } else {
+ switch (options.m_synthetic_children_traversal) {
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None:
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ FromSynthetic:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ ToSynthetic:
+ if (!root->IsSynthetic()) {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ Both:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ } else {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ }
+ }
+
+ // if we are here and options.m_no_synthetic_children is true,
+ // child_valobj_sp is going to be a NULL SP, so we hit the "else"
+ // branch, and return an error
+ if (child_valobj_sp.get()) // if it worked, just return
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEndOfString;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ return child_valobj_sp;
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ } else // other layers do expand
+ {
+ llvm::StringRef next_separator = temp_expression.substr(next_sep_pos);
+
+ child_name.SetString(temp_expression.slice(0, next_sep_pos));
+
+ ValueObjectSP child_valobj_sp =
+ root->GetChildMemberWithName(child_name, true);
+ if (child_valobj_sp.get()) // store the new root and move on
+ {
+ root = child_valobj_sp;
+ remainder = next_separator;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ } else {
+ switch (options.m_synthetic_children_traversal) {
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None:
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ FromSynthetic:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ ToSynthetic:
+ if (!root->IsSynthetic()) {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ Both:
+ if (root->IsSynthetic()) {
+ child_valobj_sp = root->GetNonSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ } else {
+ child_valobj_sp = root->GetSyntheticValue();
+ if (child_valobj_sp.get())
+ child_valobj_sp =
+ child_valobj_sp->GetChildMemberWithName(child_name, true);
+ }
+ break;
+ }
+ }
+
+ // if we are here and options.m_no_synthetic_children is true,
+ // child_valobj_sp is going to be a NULL SP, so we hit the "else"
+ // branch, and return an error
+ if (child_valobj_sp.get()) // if it worked, move on
+ {
+ root = child_valobj_sp;
+ remainder = next_separator;
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ }
+ break;
+ }
+ case '[': {
+ if (!root_compiler_type_info.Test(eTypeIsArray) &&
+ !root_compiler_type_info.Test(eTypeIsPointer) &&
+ !root_compiler_type_info.Test(
+ eTypeIsVector)) // if this is not a T[] nor a T*
+ {
+ if (!root_compiler_type_info.Test(
+ eTypeIsScalar)) // if this is not even a scalar...
+ {
+ if (options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
+ None) // ...only chance left is synthetic
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ } else if (!options.m_allow_bitfields_syntax) // if this is a scalar,
+ // check that we can
+ // expand bitfields
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ }
+ if (temp_expression[1] ==
+ ']') // if this is an unbounded range it only works for arrays
+ {
+ if (!root_compiler_type_info.Test(eTypeIsArray)) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else // even if something follows, we cannot expand unbounded ranges,
+ // just let the caller do it
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
+ *final_result =
+ ValueObject::eExpressionPathEndResultTypeUnboundedRange;
+ return root;
+ }
+ }
+
+ size_t close_bracket_position = temp_expression.find(']', 1);
+ if (close_bracket_position ==
+ llvm::StringRef::npos) // if there is no ], this is a syntax error
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+
+ llvm::StringRef bracket_expr =
+ temp_expression.slice(1, close_bracket_position);
+
+ // If this was an empty expression it would have been caught by the if
+ // above.
+ assert(!bracket_expr.empty());
+
+ if (!bracket_expr.contains('-')) {
+ // if no separator, this is of the form [N]. Note that this cannot be
+ // an unbounded range of the form [], because that case was handled
+ // above with an unconditional return.
+ unsigned long index = 0;
+ if (bracket_expr.getAsInteger(0, index)) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+
+ // from here on we do have a valid index
+ if (root_compiler_type_info.Test(eTypeIsArray)) {
+ ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true);
+ if (!child_valobj_sp)
+ child_valobj_sp = root->GetSyntheticArrayMember(index, true);
+ if (!child_valobj_sp)
+ if (root->HasSyntheticValue() &&
+ root->GetSyntheticValue()->GetNumChildren() > index)
+ child_valobj_sp =
+ root->GetSyntheticValue()->GetChildAtIndex(index, true);
+ if (child_valobj_sp) {
+ root = child_valobj_sp;
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ } else if (root_compiler_type_info.Test(eTypeIsPointer)) {
+ if (*what_next ==
+ ValueObject::
+ eExpressionPathAftermathDereference && // if this is a
+ // ptr-to-scalar, I
+ // am accessing it
+ // by index and I
+ // would have
+ // deref'ed anyway,
+ // then do it now
+ // and use this as
+ // a bitfield
+ pointee_compiler_type_info.Test(eTypeIsScalar)) {
+ Status error;
+ root = root->Dereference(error);
+ if (error.Fail() || !root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ *what_next = eExpressionPathAftermathNothing;
+ continue;
+ }
+ } else {
+ if (root->GetCompilerType().GetMinimumLanguage() ==
+ eLanguageTypeObjC &&
+ pointee_compiler_type_info.AllClear(eTypeIsPointer) &&
+ root->HasSyntheticValue() &&
+ (options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::ToSynthetic ||
+ options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::Both)) {
+ root = root->GetSyntheticValue()->GetChildAtIndex(index, true);
+ } else
+ root = root->GetSyntheticArrayMember(index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ }
+ }
+ } else if (root_compiler_type_info.Test(eTypeIsScalar)) {
+ root = root->GetSyntheticBitFieldChild(index, index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else // we do not know how to expand members of bitfields, so we
+ // just return and let the caller do any further processing
+ {
+ *reason_to_stop = ValueObject::
+ eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
+ *final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
+ return root;
+ }
+ } else if (root_compiler_type_info.Test(eTypeIsVector)) {
+ root = root->GetChildAtIndex(index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ } else {
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ }
+ } else if (options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::ToSynthetic ||
+ options.m_synthetic_children_traversal ==
+ GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::Both) {
+ if (root->HasSyntheticValue())
+ root = root->GetSyntheticValue();
+ else if (!root->IsSynthetic()) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ // if we are here, then root itself is a synthetic VO.. should be
+ // good to go
+
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ root = root->GetChildAtIndex(index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ remainder =
+ temp_expression.substr(close_bracket_position + 1); // skip ]
+ *final_result = ValueObject::eExpressionPathEndResultTypePlain;
+ continue;
+ }
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ } else {
+ // we have a low and a high index
+ llvm::StringRef sleft, sright;
+ unsigned long low_index, high_index;
+ std::tie(sleft, sright) = bracket_expr.split('-');
+ if (sleft.getAsInteger(0, low_index) ||
+ sright.getAsInteger(0, high_index)) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+
+ if (low_index > high_index) // swap indices if required
+ std::swap(low_index, high_index);
+
+ if (root_compiler_type_info.Test(
+ eTypeIsScalar)) // expansion only works for scalars
+ {
+ root = root->GetSyntheticBitFieldChild(low_index, high_index, true);
+ if (!root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonNoSuchChild;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ *reason_to_stop = ValueObject::
+ eExpressionPathScanEndReasonBitfieldRangeOperatorMet;
+ *final_result = ValueObject::eExpressionPathEndResultTypeBitfield;
+ return root;
+ }
+ } else if (root_compiler_type_info.Test(
+ eTypeIsPointer) && // if this is a ptr-to-scalar, I am
+ // accessing it by index and I would
+ // have deref'ed anyway, then do it
+ // now and use this as a bitfield
+ *what_next ==
+ ValueObject::eExpressionPathAftermathDereference &&
+ pointee_compiler_type_info.Test(eTypeIsScalar)) {
+ Status error;
+ root = root->Dereference(error);
+ if (error.Fail() || !root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ } else {
+ *what_next = ValueObject::eExpressionPathAftermathNothing;
+ continue;
+ }
+ } else {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet;
+ *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange;
+ return root;
+ }
+ }
+ break;
+ }
+ default: // some non-separator is in the way
+ {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return nullptr;
+ }
+ }
+ }
+}
+
+void ValueObject::LogValueObject(Log *log) {
+ if (log)
+ return LogValueObject(log, DumpValueObjectOptions(*this));
+}
+
+void ValueObject::LogValueObject(Log *log,
+ const DumpValueObjectOptions &options) {
+ if (log) {
+ StreamString s;
+ Dump(s, options);
+ if (s.GetSize())
+ log->PutCString(s.GetData());
+ }
+}
+
+void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); }
+
+void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) {
+ ValueObjectPrinter printer(this, &s, options);
+ printer.PrintValueObject();
+}
+
+ValueObjectSP ValueObject::CreateConstantValue(ConstString name) {
+ ValueObjectSP valobj_sp;
+
+ if (UpdateValueIfNeeded(false) && m_error.Success()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ DataExtractor data;
+ data.SetByteOrder(m_data.GetByteOrder());
+ data.SetAddressByteSize(m_data.GetAddressByteSize());
+
+ if (IsBitfield()) {
+ Value v(Scalar(GetValueAsUnsigned(UINT64_MAX)));
+ m_error = v.GetValueAsData(&exe_ctx, data, GetModule().get());
+ } else
+ m_error = m_value.GetValueAsData(&exe_ctx, data, GetModule().get());
+
+ valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), GetCompilerType(), name, data,
+ GetAddressOf());
+ }
+
+ if (!valobj_sp) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), m_error);
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP ValueObject::GetQualifiedRepresentationIfAvailable(
+ lldb::DynamicValueType dynValue, bool synthValue) {
+ ValueObjectSP result_sp(GetSP());
+
+ switch (dynValue) {
+ case lldb::eDynamicCanRunTarget:
+ case lldb::eDynamicDontRunTarget: {
+ if (!result_sp->IsDynamic()) {
+ if (result_sp->GetDynamicValue(dynValue))
+ result_sp = result_sp->GetDynamicValue(dynValue);
+ }
+ } break;
+ case lldb::eNoDynamicValues: {
+ if (result_sp->IsDynamic()) {
+ if (result_sp->GetStaticValue())
+ result_sp = result_sp->GetStaticValue();
+ }
+ } break;
+ }
+
+ if (synthValue) {
+ if (!result_sp->IsSynthetic()) {
+ if (result_sp->GetSyntheticValue())
+ result_sp = result_sp->GetSyntheticValue();
+ }
+ } else {
+ if (result_sp->IsSynthetic()) {
+ if (result_sp->GetNonSyntheticValue())
+ result_sp = result_sp->GetNonSyntheticValue();
+ }
+ }
+
+ return result_sp;
+}
+
+ValueObjectSP ValueObject::Dereference(Status &error) {
+ if (m_deref_valobj)
+ return m_deref_valobj->GetSP();
+
+ const bool is_pointer_or_reference_type = IsPointerOrReferenceType();
+ if (is_pointer_or_reference_type) {
+ bool omit_empty_base_classes = true;
+ bool ignore_array_bounds = false;
+
+ std::string child_name_str;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ const bool transparent_pointers = false;
+ CompilerType compiler_type = GetCompilerType();
+ CompilerType child_compiler_type;
+ uint64_t language_flags;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ child_compiler_type = compiler_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, 0, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, this, language_flags);
+ if (child_compiler_type && child_byte_size) {
+ ConstString child_name;
+ if (!child_name_str.empty())
+ child_name.SetCString(child_name_str.c_str());
+
+ m_deref_valobj = new ValueObjectChild(
+ *this, child_compiler_type, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid,
+ language_flags);
+ }
+ } else if (HasSyntheticValue()) {
+ m_deref_valobj =
+ GetSyntheticValue()
+ ->GetChildMemberWithName(ConstString("$$dereference$$"), true)
+ .get();
+ }
+
+ if (m_deref_valobj) {
+ error.Clear();
+ return m_deref_valobj->GetSP();
+ } else {
+ StreamString strm;
+ GetExpressionPath(strm, true);
+
+ if (is_pointer_or_reference_type)
+ error.SetErrorStringWithFormat("dereference failed: (%s) %s",
+ GetTypeName().AsCString("<invalid type>"),
+ strm.GetData());
+ else
+ error.SetErrorStringWithFormat("not a pointer or reference type: (%s) %s",
+ GetTypeName().AsCString("<invalid type>"),
+ strm.GetData());
+ return ValueObjectSP();
+ }
+}
+
+ValueObjectSP ValueObject::AddressOf(Status &error) {
+ if (m_addr_of_valobj_sp)
+ return m_addr_of_valobj_sp;
+
+ AddressType address_type = eAddressTypeInvalid;
+ const bool scalar_is_load_address = false;
+ addr_t addr = GetAddressOf(scalar_is_load_address, &address_type);
+ error.Clear();
+ if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) {
+ switch (address_type) {
+ case eAddressTypeInvalid: {
+ StreamString expr_path_strm;
+ GetExpressionPath(expr_path_strm, true);
+ error.SetErrorStringWithFormat("'%s' is not in memory",
+ expr_path_strm.GetData());
+ } break;
+
+ case eAddressTypeFile:
+ case eAddressTypeLoad: {
+ CompilerType compiler_type = GetCompilerType();
+ if (compiler_type) {
+ std::string name(1, '&');
+ name.append(m_name.AsCString(""));
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ m_addr_of_valobj_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(),
+ compiler_type.GetPointerType(), ConstString(name.c_str()), addr,
+ eAddressTypeInvalid, m_data.GetAddressByteSize());
+ }
+ } break;
+ default:
+ break;
+ }
+ } else {
+ StreamString expr_path_strm;
+ GetExpressionPath(expr_path_strm, true);
+ error.SetErrorStringWithFormat("'%s' doesn't have a valid address",
+ expr_path_strm.GetData());
+ }
+
+ return m_addr_of_valobj_sp;
+}
+
+ValueObjectSP ValueObject::Cast(const CompilerType &compiler_type) {
+ return ValueObjectCast::Create(*this, GetName(), compiler_type);
+}
+
+lldb::ValueObjectSP ValueObject::Clone(ConstString new_name) {
+ return ValueObjectCast::Create(*this, new_name, GetCompilerType());
+}
+
+ValueObjectSP ValueObject::CastPointerType(const char *name,
+ CompilerType &compiler_type) {
+ ValueObjectSP valobj_sp;
+ AddressType address_type;
+ addr_t ptr_value = GetPointerValue(&address_type);
+
+ if (ptr_value != LLDB_INVALID_ADDRESS) {
+ Address ptr_addr(ptr_value);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ valobj_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, compiler_type);
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) {
+ ValueObjectSP valobj_sp;
+ AddressType address_type;
+ addr_t ptr_value = GetPointerValue(&address_type);
+
+ if (ptr_value != LLDB_INVALID_ADDRESS) {
+ Address ptr_addr(ptr_value);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ valobj_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), name, ptr_addr, type_sp);
+ }
+ return valobj_sp;
+}
+
+ValueObject::EvaluationPoint::EvaluationPoint()
+ : m_mod_id(), m_exe_ctx_ref(), m_needs_update(true) {}
+
+ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope,
+ bool use_selected)
+ : m_mod_id(), m_exe_ctx_ref(), m_needs_update(true) {
+ ExecutionContext exe_ctx(exe_scope);
+ TargetSP target_sp(exe_ctx.GetTargetSP());
+ if (target_sp) {
+ m_exe_ctx_ref.SetTargetSP(target_sp);
+ ProcessSP process_sp(exe_ctx.GetProcessSP());
+ if (!process_sp)
+ process_sp = target_sp->GetProcessSP();
+
+ if (process_sp) {
+ m_mod_id = process_sp->GetModID();
+ m_exe_ctx_ref.SetProcessSP(process_sp);
+
+ ThreadSP thread_sp(exe_ctx.GetThreadSP());
+
+ if (!thread_sp) {
+ if (use_selected)
+ thread_sp = process_sp->GetThreadList().GetSelectedThread();
+ }
+
+ if (thread_sp) {
+ m_exe_ctx_ref.SetThreadSP(thread_sp);
+
+ StackFrameSP frame_sp(exe_ctx.GetFrameSP());
+ if (!frame_sp) {
+ if (use_selected)
+ frame_sp = thread_sp->GetSelectedFrame();
+ }
+ if (frame_sp)
+ m_exe_ctx_ref.SetFrameSP(frame_sp);
+ }
+ }
+ }
+}
+
+ValueObject::EvaluationPoint::EvaluationPoint(
+ const ValueObject::EvaluationPoint &rhs)
+ : m_mod_id(), m_exe_ctx_ref(rhs.m_exe_ctx_ref), m_needs_update(true) {}
+
+ValueObject::EvaluationPoint::~EvaluationPoint() {}
+
+// This function checks the EvaluationPoint against the current process state.
+// If the current state matches the evaluation point, or the evaluation point
+// is already invalid, then we return false, meaning "no change". If the
+// current state is different, we update our state, and return true meaning
+// "yes, change". If we did see a change, we also set m_needs_update to true,
+// so future calls to NeedsUpdate will return true. exe_scope will be set to
+// the current execution context scope.
+
+bool ValueObject::EvaluationPoint::SyncWithProcessState(
+ bool accept_invalid_exe_ctx) {
+ // Start with the target, if it is NULL, then we're obviously not going to
+ // get any further:
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx(
+ m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped));
+
+ if (exe_ctx.GetTargetPtr() == nullptr)
+ return false;
+
+ // If we don't have a process nothing can change.
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == nullptr)
+ return false;
+
+ // If our stop id is the current stop ID, nothing has changed:
+ ProcessModID current_mod_id = process->GetModID();
+
+ // If the current stop id is 0, either we haven't run yet, or the process
+ // state has been cleared. In either case, we aren't going to be able to sync
+ // with the process state.
+ if (current_mod_id.GetStopID() == 0)
+ return false;
+
+ bool changed = false;
+ const bool was_valid = m_mod_id.IsValid();
+ if (was_valid) {
+ if (m_mod_id == current_mod_id) {
+ // Everything is already up to date in this object, no need to update the
+ // execution context scope.
+ changed = false;
+ } else {
+ m_mod_id = current_mod_id;
+ m_needs_update = true;
+ changed = true;
+ }
+ }
+
+ // Now re-look up the thread and frame in case the underlying objects have
+ // gone away & been recreated. That way we'll be sure to return a valid
+ // exe_scope. If we used to have a thread or a frame but can't find it
+ // anymore, then mark ourselves as invalid.
+
+ if (!accept_invalid_exe_ctx) {
+ if (m_exe_ctx_ref.HasThreadRef()) {
+ ThreadSP thread_sp(m_exe_ctx_ref.GetThreadSP());
+ if (thread_sp) {
+ if (m_exe_ctx_ref.HasFrameRef()) {
+ StackFrameSP frame_sp(m_exe_ctx_ref.GetFrameSP());
+ if (!frame_sp) {
+ // We used to have a frame, but now it is gone
+ SetInvalid();
+ changed = was_valid;
+ }
+ }
+ } else {
+ // We used to have a thread, but now it is gone
+ SetInvalid();
+ changed = was_valid;
+ }
+ }
+ }
+
+ return changed;
+}
+
+void ValueObject::EvaluationPoint::SetUpdated() {
+ ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+ if (process_sp)
+ m_mod_id = process_sp->GetModID();
+ m_needs_update = false;
+}
+
+void ValueObject::ClearUserVisibleData(uint32_t clear_mask) {
+ if ((clear_mask & eClearUserVisibleDataItemsValue) ==
+ eClearUserVisibleDataItemsValue)
+ m_value_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsLocation) ==
+ eClearUserVisibleDataItemsLocation)
+ m_location_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsSummary) ==
+ eClearUserVisibleDataItemsSummary)
+ m_summary_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsDescription) ==
+ eClearUserVisibleDataItemsDescription)
+ m_object_desc_str.clear();
+
+ if ((clear_mask & eClearUserVisibleDataItemsSyntheticChildren) ==
+ eClearUserVisibleDataItemsSyntheticChildren) {
+ if (m_synthetic_value)
+ m_synthetic_value = nullptr;
+ }
+
+ if ((clear_mask & eClearUserVisibleDataItemsValidator) ==
+ eClearUserVisibleDataItemsValidator)
+ m_validation_result.reset();
+}
+
+SymbolContextScope *ValueObject::GetSymbolContextScope() {
+ if (m_parent) {
+ if (!m_parent->IsPointerOrReferenceType())
+ return m_parent->GetSymbolContextScope();
+ }
+ return nullptr;
+}
+
+lldb::ValueObjectSP
+ValueObject::CreateValueObjectFromExpression(llvm::StringRef name,
+ llvm::StringRef expression,
+ const ExecutionContext &exe_ctx) {
+ return CreateValueObjectFromExpression(name, expression, exe_ctx,
+ EvaluateExpressionOptions());
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression(
+ llvm::StringRef name, llvm::StringRef expression,
+ const ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options) {
+ lldb::ValueObjectSP retval_sp;
+ lldb::TargetSP target_sp(exe_ctx.GetTargetSP());
+ if (!target_sp)
+ return retval_sp;
+ if (expression.empty())
+ return retval_sp;
+ target_sp->EvaluateExpression(expression, exe_ctx.GetFrameSP().get(),
+ retval_sp, options);
+ if (retval_sp && !name.empty())
+ retval_sp->SetName(ConstString(name));
+ return retval_sp;
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress(
+ llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx,
+ CompilerType type) {
+ if (type) {
+ CompilerType pointer_type(type.GetPointerType());
+ if (pointer_type) {
+ lldb::DataBufferSP buffer(
+ new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t)));
+ lldb::ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), pointer_type,
+ ConstString(name), buffer, exe_ctx.GetByteOrder(),
+ exe_ctx.GetAddressByteSize()));
+ if (ptr_result_valobj_sp) {
+ ptr_result_valobj_sp->GetValue().SetValueType(
+ Value::eValueTypeLoadAddress);
+ Status err;
+ ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err);
+ if (ptr_result_valobj_sp && !name.empty())
+ ptr_result_valobj_sp->SetName(ConstString(name));
+ }
+ return ptr_result_valobj_sp;
+ }
+ }
+ return lldb::ValueObjectSP();
+}
+
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromData(
+ llvm::StringRef name, const DataExtractor &data,
+ const ExecutionContext &exe_ctx, CompilerType type) {
+ lldb::ValueObjectSP new_value_sp;
+ new_value_sp = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), type, ConstString(name), data,
+ LLDB_INVALID_ADDRESS);
+ new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad);
+ if (new_value_sp && !name.empty())
+ new_value_sp->SetName(ConstString(name));
+ return new_value_sp;
+}
+
+ModuleSP ValueObject::GetModule() {
+ ValueObject *root(GetRoot());
+ if (root != this)
+ return root->GetModule();
+ return lldb::ModuleSP();
+}
+
+ValueObject *ValueObject::GetRoot() {
+ if (m_root)
+ return m_root;
+ return (m_root = FollowParentChain([](ValueObject *vo) -> bool {
+ return (vo->m_parent != nullptr);
+ }));
+}
+
+ValueObject *
+ValueObject::FollowParentChain(std::function<bool(ValueObject *)> f) {
+ ValueObject *vo = this;
+ while (vo) {
+ if (!f(vo))
+ break;
+ vo = vo->m_parent;
+ }
+ return vo;
+}
+
+AddressType ValueObject::GetAddressTypeOfChildren() {
+ if (m_address_type_of_ptr_or_ref_children == eAddressTypeInvalid) {
+ ValueObject *root(GetRoot());
+ if (root != this)
+ return root->GetAddressTypeOfChildren();
+ }
+ return m_address_type_of_ptr_or_ref_children;
+}
+
+lldb::DynamicValueType ValueObject::GetDynamicValueType() {
+ ValueObject *with_dv_info = this;
+ while (with_dv_info) {
+ if (with_dv_info->HasDynamicValueTypeInfo())
+ return with_dv_info->GetDynamicValueTypeImpl();
+ with_dv_info = with_dv_info->m_parent;
+ }
+ return lldb::eNoDynamicValues;
+}
+
+lldb::Format ValueObject::GetFormat() const {
+ const ValueObject *with_fmt_info = this;
+ while (with_fmt_info) {
+ if (with_fmt_info->m_format != lldb::eFormatDefault)
+ return with_fmt_info->m_format;
+ with_fmt_info = with_fmt_info->m_parent;
+ }
+ return m_format;
+}
+
+lldb::LanguageType ValueObject::GetPreferredDisplayLanguage() {
+ lldb::LanguageType type = m_preferred_display_language;
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
+ if (GetRoot()) {
+ if (GetRoot() == this) {
+ if (StackFrameSP frame_sp = GetFrameSP()) {
+ const SymbolContext &sc(
+ frame_sp->GetSymbolContext(eSymbolContextCompUnit));
+ if (CompileUnit *cu = sc.comp_unit)
+ type = cu->GetLanguage();
+ }
+ } else {
+ type = GetRoot()->GetPreferredDisplayLanguage();
+ }
+ }
+ }
+ return (m_preferred_display_language = type); // only compute it once
+}
+
+void ValueObject::SetPreferredDisplayLanguage(lldb::LanguageType lt) {
+ m_preferred_display_language = lt;
+}
+
+void ValueObject::SetPreferredDisplayLanguageIfNeeded(lldb::LanguageType lt) {
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown)
+ SetPreferredDisplayLanguage(lt);
+}
+
+bool ValueObject::CanProvideValue() {
+ // we need to support invalid types as providers of values because some bare-
+ // board debugging scenarios have no notion of types, but still manage to
+ // have raw numeric values for things like registers. sigh.
+ const CompilerType &type(GetCompilerType());
+ return (!type.IsValid()) || (0 != (type.GetTypeInfo() & eTypeHasValue));
+}
+
+bool ValueObject::IsChecksumEmpty() { return m_value_checksum.empty(); }
+
+ValueObjectSP ValueObject::Persist() {
+ if (!UpdateValueIfNeeded())
+ return nullptr;
+
+ TargetSP target_sp(GetTargetSP());
+ if (!target_sp)
+ return nullptr;
+
+ PersistentExpressionState *persistent_state =
+ target_sp->GetPersistentExpressionStateForLanguage(
+ GetPreferredDisplayLanguage());
+
+ if (!persistent_state)
+ return nullptr;
+
+ auto prefix = persistent_state->GetPersistentVariablePrefix();
+ ConstString name =
+ persistent_state->GetNextPersistentVariableName(*target_sp, prefix);
+
+ ValueObjectSP const_result_sp =
+ ValueObjectConstResult::Create(target_sp.get(), GetValue(), name);
+
+ ExpressionVariableSP clang_var_sp =
+ persistent_state->CreatePersistentVariable(const_result_sp);
+ clang_var_sp->m_live_sp = clang_var_sp->m_frozen_sp;
+ clang_var_sp->m_flags |= ExpressionVariable::EVIsProgramReference;
+
+ return clang_var_sp->GetValueObject();
+}
+
+bool ValueObject::IsSyntheticChildrenGenerated() {
+ return m_is_synthetic_children_generated;
+}
+
+void ValueObject::SetSyntheticChildrenGenerated(bool b) {
+ m_is_synthetic_children_generated = b;
+}
+
+uint64_t ValueObject::GetLanguageFlags() { return m_language_flags; }
+
+void ValueObject::SetLanguageFlags(uint64_t flags) { m_language_flags = flags; }
+
+ValueObjectManager::ValueObjectManager(lldb::ValueObjectSP in_valobj_sp,
+ lldb::DynamicValueType use_dynamic,
+ bool use_synthetic) : m_root_valobj_sp(),
+ m_user_valobj_sp(), m_use_dynamic(use_dynamic), m_stop_id(UINT32_MAX),
+ m_use_synthetic(use_synthetic) {
+ if (!in_valobj_sp)
+ return;
+ // If the user passes in a value object that is dynamic or synthetic, then
+ // water it down to the static type.
+ m_root_valobj_sp = in_valobj_sp->GetQualifiedRepresentationIfAvailable(lldb::eNoDynamicValues, false);
+}
+
+bool ValueObjectManager::IsValid() const {
+ if (!m_root_valobj_sp)
+ return false;
+ lldb::TargetSP target_sp = GetTargetSP();
+ if (target_sp)
+ return target_sp->IsValid();
+ return false;
+}
+
+lldb::ValueObjectSP ValueObjectManager::GetSP() {
+ lldb::ProcessSP process_sp = GetProcessSP();
+ if (!process_sp)
+ return lldb::ValueObjectSP();
+
+ const uint32_t current_stop_id = process_sp->GetLastNaturalStopID();
+ if (current_stop_id == m_stop_id)
+ return m_user_valobj_sp;
+
+ m_stop_id = current_stop_id;
+
+ if (!m_root_valobj_sp) {
+ m_user_valobj_sp.reset();
+ return m_root_valobj_sp;
+ }
+
+ m_user_valobj_sp = m_root_valobj_sp;
+
+ if (m_use_dynamic != lldb::eNoDynamicValues) {
+ lldb::ValueObjectSP dynamic_sp = m_user_valobj_sp->GetDynamicValue(m_use_dynamic);
+ if (dynamic_sp)
+ m_user_valobj_sp = dynamic_sp;
+ }
+
+ if (m_use_synthetic) {
+ lldb::ValueObjectSP synthetic_sp = m_user_valobj_sp->GetSyntheticValue(m_use_synthetic);
+ if (synthetic_sp)
+ m_user_valobj_sp = synthetic_sp;
+ }
+
+ return m_user_valobj_sp;
+}
+
+void ValueObjectManager::SetUseDynamic(lldb::DynamicValueType use_dynamic) {
+ if (use_dynamic != m_use_dynamic) {
+ m_use_dynamic = use_dynamic;
+ m_user_valobj_sp.reset();
+ m_stop_id = UINT32_MAX;
+ }
+}
+
+void ValueObjectManager::SetUseSynthetic(bool use_synthetic) {
+ if (m_use_synthetic != use_synthetic) {
+ m_use_synthetic = use_synthetic;
+ m_user_valobj_sp.reset();
+ m_stop_id = UINT32_MAX;
+ }
+}
+
+lldb::TargetSP ValueObjectManager::GetTargetSP() const {
+ if (!m_root_valobj_sp)
+ return m_root_valobj_sp->GetTargetSP();
+ return lldb::TargetSP();
+}
+
+lldb::ProcessSP ValueObjectManager::GetProcessSP() const {
+ if (m_root_valobj_sp)
+ return m_root_valobj_sp->GetProcessSP();
+ return lldb::ProcessSP();
+}
+
+lldb::ThreadSP ValueObjectManager::GetThreadSP() const {
+ if (m_root_valobj_sp)
+ return m_root_valobj_sp->GetThreadSP();
+ return lldb::ThreadSP();
+}
+
+lldb::StackFrameSP ValueObjectManager::GetFrameSP() const {
+ if (m_root_valobj_sp)
+ return m_root_valobj_sp->GetFrameSP();
+ return lldb::StackFrameSP();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp
new file mode 100644
index 000000000000..3a74b6a7fe18
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectCast.cpp
@@ -0,0 +1,94 @@
+//===-- ValueObjectCast.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectCast.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+class ConstString;
+}
+
+using namespace lldb_private;
+
+lldb::ValueObjectSP ValueObjectCast::Create(ValueObject &parent,
+ ConstString name,
+ const CompilerType &cast_type) {
+ ValueObjectCast *cast_valobj_ptr =
+ new ValueObjectCast(parent, name, cast_type);
+ return cast_valobj_ptr->GetSP();
+}
+
+ValueObjectCast::ValueObjectCast(ValueObject &parent, ConstString name,
+ const CompilerType &cast_type)
+ : ValueObject(parent), m_cast_type(cast_type) {
+ SetName(name);
+ // m_value.SetContext (Value::eContextTypeClangType,
+ // cast_type.GetOpaqueQualType());
+ m_value.SetCompilerType(cast_type);
+}
+
+ValueObjectCast::~ValueObjectCast() {}
+
+CompilerType ValueObjectCast::GetCompilerTypeImpl() { return m_cast_type; }
+
+size_t ValueObjectCast::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(
+ true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+uint64_t ValueObjectCast::GetByteSize() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ return m_value.GetValueByteSize(nullptr, &exe_ctx);
+}
+
+lldb::ValueType ValueObjectCast::GetValueType() const {
+ // Let our parent answer global, local, argument, etc...
+ return m_parent->GetValueType();
+}
+
+bool ValueObjectCast::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ if (m_parent->UpdateValueIfNeeded(false)) {
+ Value old_value(m_value);
+ m_update_point.SetUpdated();
+ m_value = m_parent->GetValue();
+ CompilerType compiler_type(GetCompilerType());
+ // m_value.SetContext (Value::eContextTypeClangType, compiler_type);
+ m_value.SetCompilerType(compiler_type);
+ SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren());
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(m_value.GetValueType() != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ }
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ SetValueDidChange(m_parent->GetValueDidChange());
+ return true;
+ }
+
+ // The dynamic value failed to get an error, pass the error along
+ if (m_error.Success() && m_parent->GetError().Fail())
+ m_error = m_parent->GetError();
+ SetValueIsValid(false);
+ return false;
+}
+
+bool ValueObjectCast::IsInScope() { return m_parent->IsInScope(); }
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp
new file mode 100644
index 000000000000..6b4ada154d68
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectChild.cpp
@@ -0,0 +1,252 @@
+//===-- ValueObjectChild.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectChild.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-forward.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <stdio.h>
+#include <string.h>
+
+using namespace lldb_private;
+
+ValueObjectChild::ValueObjectChild(
+ ValueObject &parent, const CompilerType &compiler_type,
+ ConstString name, uint64_t byte_size, int32_t byte_offset,
+ uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
+ bool is_base_class, bool is_deref_of_parent,
+ AddressType child_ptr_or_ref_addr_type, uint64_t language_flags)
+ : ValueObject(parent), m_compiler_type(compiler_type),
+ m_byte_size(byte_size), m_byte_offset(byte_offset),
+ m_bitfield_bit_size(bitfield_bit_size),
+ m_bitfield_bit_offset(bitfield_bit_offset),
+ m_is_base_class(is_base_class), m_is_deref_of_parent(is_deref_of_parent),
+ m_can_update_with_invalid_exe_ctx() {
+ m_name = name;
+ SetAddressTypeOfChildren(child_ptr_or_ref_addr_type);
+ SetLanguageFlags(language_flags);
+}
+
+ValueObjectChild::~ValueObjectChild() {}
+
+lldb::ValueType ValueObjectChild::GetValueType() const {
+ return m_parent->GetValueType();
+}
+
+size_t ValueObjectChild::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+static void AdjustForBitfieldness(ConstString &name,
+ uint8_t bitfield_bit_size) {
+ if (name && bitfield_bit_size) {
+ const char *compiler_type_name = name.AsCString();
+ if (compiler_type_name) {
+ std::vector<char> bitfield_type_name(strlen(compiler_type_name) + 32, 0);
+ ::snprintf(&bitfield_type_name.front(), bitfield_type_name.size(),
+ "%s:%u", compiler_type_name, bitfield_bit_size);
+ name.SetCString(&bitfield_type_name.front());
+ }
+ }
+}
+
+ConstString ValueObjectChild::GetTypeName() {
+ if (m_type_name.IsEmpty()) {
+ m_type_name = GetCompilerType().GetConstTypeName();
+ AdjustForBitfieldness(m_type_name, m_bitfield_bit_size);
+ }
+ return m_type_name;
+}
+
+ConstString ValueObjectChild::GetQualifiedTypeName() {
+ ConstString qualified_name = GetCompilerType().GetConstTypeName();
+ AdjustForBitfieldness(qualified_name, m_bitfield_bit_size);
+ return qualified_name;
+}
+
+ConstString ValueObjectChild::GetDisplayTypeName() {
+ ConstString display_name = GetCompilerType().GetDisplayTypeName();
+ AdjustForBitfieldness(display_name, m_bitfield_bit_size);
+ return display_name;
+}
+
+LazyBool ValueObjectChild::CanUpdateWithInvalidExecutionContext() {
+ if (m_can_update_with_invalid_exe_ctx.hasValue())
+ return m_can_update_with_invalid_exe_ctx.getValue();
+ if (m_parent) {
+ ValueObject *opinionated_parent =
+ m_parent->FollowParentChain([](ValueObject *valobj) -> bool {
+ return (valobj->CanUpdateWithInvalidExecutionContext() ==
+ eLazyBoolCalculate);
+ });
+ if (opinionated_parent)
+ return (m_can_update_with_invalid_exe_ctx =
+ opinionated_parent->CanUpdateWithInvalidExecutionContext())
+ .getValue();
+ }
+ return (m_can_update_with_invalid_exe_ctx =
+ this->ValueObject::CanUpdateWithInvalidExecutionContext())
+ .getValue();
+}
+
+bool ValueObjectChild::UpdateValue() {
+ m_error.Clear();
+ SetValueIsValid(false);
+ ValueObject *parent = m_parent;
+ if (parent) {
+ if (parent->UpdateValueIfNeeded(false)) {
+ m_value.SetCompilerType(GetCompilerType());
+
+ CompilerType parent_type(parent->GetCompilerType());
+ // Copy the parent scalar value and the scalar value type
+ m_value.GetScalar() = parent->GetValue().GetScalar();
+ Value::ValueType value_type = parent->GetValue().GetValueType();
+ m_value.SetValueType(value_type);
+
+ Flags parent_type_flags(parent_type.GetTypeInfo());
+ const bool is_instance_ptr_base =
+ ((m_is_base_class) &&
+ (parent_type_flags.AnySet(lldb::eTypeInstanceIsPointer)));
+
+ if (parent->GetCompilerType().ShouldTreatScalarValueAsAddress()) {
+ lldb::addr_t addr = parent->GetPointerValue();
+ m_value.GetScalar() = addr;
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent address is invalid.");
+ } else if (addr == 0) {
+ m_error.SetErrorString("parent is NULL");
+ } else {
+ m_value.GetScalar() += m_byte_offset;
+ AddressType addr_type = parent->GetAddressTypeOfChildren();
+
+ switch (addr_type) {
+ case eAddressTypeFile: {
+ lldb::ProcessSP process_sp(GetProcessSP());
+ if (process_sp && process_sp->IsAlive())
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ else
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ } break;
+ case eAddressTypeLoad:
+ m_value.SetValueType(is_instance_ptr_base
+ ? Value::eValueTypeScalar
+ : Value::eValueTypeLoadAddress);
+ break;
+ case eAddressTypeHost:
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ break;
+ case eAddressTypeInvalid:
+ // TODO: does this make sense?
+ m_value.SetValueType(Value::eValueTypeScalar);
+ break;
+ }
+ }
+ } else {
+ switch (value_type) {
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeHostAddress: {
+ lldb::addr_t addr =
+ m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+ if (addr == LLDB_INVALID_ADDRESS) {
+ m_error.SetErrorString("parent address is invalid.");
+ } else if (addr == 0) {
+ m_error.SetErrorString("parent is NULL");
+ } else {
+ // Set this object's scalar value to the address of its value by
+ // adding its byte offset to the parent address
+ m_value.GetScalar() += GetByteOffset();
+
+ // If a bitfield doesn't fit into the child_byte_size'd
+ // window at child_byte_offset, move the window forward
+ // until it fits. The problem here is that Value has no
+ // notion of bitfields and thus the Value's DataExtractor
+ // is sized like the bitfields CompilerType; a sequence of
+ // bitfields, however, can be larger than their underlying
+ // type.
+ if (m_bitfield_bit_offset) {
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx(GetExecutionContextRef().Lock(
+ thread_and_frame_only_if_stopped));
+ if (auto type_bit_size = GetCompilerType().GetBitSize(
+ exe_ctx.GetBestExecutionContextScope())) {
+ uint64_t bitfield_end =
+ m_bitfield_bit_size + m_bitfield_bit_offset;
+ if (bitfield_end > *type_bit_size) {
+ uint64_t overhang_bytes =
+ (bitfield_end - *type_bit_size + 7) / 8;
+ m_value.GetScalar() += overhang_bytes;
+ m_bitfield_bit_offset -= overhang_bytes * 8;
+ }
+ }
+ }
+ }
+ } break;
+
+ case Value::eValueTypeScalar:
+ // try to extract the child value from the parent's scalar value
+ {
+ Scalar scalar(m_value.GetScalar());
+ if (m_bitfield_bit_size)
+ scalar.ExtractBitfield(m_bitfield_bit_size,
+ m_bitfield_bit_offset);
+ else
+ scalar.ExtractBitfield(8 * m_byte_size, 8 * m_byte_offset);
+ m_value.GetScalar() = scalar;
+ }
+ break;
+ default:
+ m_error.SetErrorString("parent has invalid value.");
+ break;
+ }
+ }
+
+ if (m_error.Success()) {
+ const bool thread_and_frame_only_if_stopped = true;
+ ExecutionContext exe_ctx(
+ GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped));
+ if (GetCompilerType().GetTypeInfo() & lldb::eTypeHasValue) {
+ Value &value = is_instance_ptr_base ? m_parent->GetValue() : m_value;
+ m_error =
+ value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ } else {
+ m_error.Clear(); // No value so nothing to read...
+ }
+ }
+
+ } else {
+ m_error.SetErrorStringWithFormat("parent failed to evaluate: %s",
+ parent->GetError().AsCString());
+ }
+ } else {
+ m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject.");
+ }
+
+ return m_error.Success();
+}
+
+bool ValueObjectChild::IsInScope() {
+ ValueObject *root(GetRoot());
+ if (root)
+ return root->IsInScope();
+ return false;
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp
new file mode 100644
index 000000000000..71620698da2a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResult.cpp
@@ -0,0 +1,296 @@
+//===-- ValueObjectConstResult.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResult.h"
+
+#include "lldb/Core/ValueObjectDynamicValue.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+
+namespace lldb_private {
+class Module;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ ByteOrder byte_order,
+ uint32_t addr_byte_size,
+ lldb::addr_t address) {
+ return (new ValueObjectConstResult(exe_scope, byte_order, addr_byte_size,
+ address))
+ ->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ ByteOrder byte_order,
+ uint32_t addr_byte_size,
+ lldb::addr_t address)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ SetIsConstant();
+ SetValueIsValid(true);
+ m_data.SetByteOrder(byte_order);
+ m_data.SetAddressByteSize(addr_byte_size);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ ConstString name,
+ const DataExtractor &data,
+ lldb::addr_t address) {
+ return (new ValueObjectConstResult(exe_scope, compiler_type, name, data,
+ address))
+ ->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, const CompilerType &compiler_type,
+ ConstString name, const DataExtractor &data, lldb::addr_t address)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ m_data = data;
+
+ if (!m_data.GetSharedDataBuffer()) {
+ DataBufferSP shared_data_buffer(
+ new DataBufferHeap(data.GetDataStart(), data.GetByteSize()));
+ m_data.SetData(shared_data_buffer);
+ }
+
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ m_value.SetCompilerType(compiler_type);
+ m_name = name;
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ ConstString name,
+ const lldb::DataBufferSP &data_sp,
+ lldb::ByteOrder data_byte_order,
+ uint32_t data_addr_size,
+ lldb::addr_t address) {
+ return (new ValueObjectConstResult(exe_scope, compiler_type, name, data_sp,
+ data_byte_order, data_addr_size, address))
+ ->GetSP();
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ Value &value,
+ ConstString name,
+ Module *module) {
+ return (new ValueObjectConstResult(exe_scope, value, name, module))->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, const CompilerType &compiler_type,
+ ConstString name, const lldb::DataBufferSP &data_sp,
+ lldb::ByteOrder data_byte_order, uint32_t data_addr_size,
+ lldb::addr_t address)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ m_data.SetByteOrder(data_byte_order);
+ m_data.SetAddressByteSize(data_addr_size);
+ m_data.SetData(data_sp);
+ m_value.GetScalar() = (uintptr_t)data_sp->GetBytes();
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ // m_value.SetContext(Value::eContextTypeClangType, compiler_type);
+ m_value.SetCompilerType(compiler_type);
+ m_name = name;
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ ConstString name,
+ lldb::addr_t address,
+ AddressType address_type,
+ uint32_t addr_byte_size) {
+ return (new ValueObjectConstResult(exe_scope, compiler_type, name, address,
+ address_type, addr_byte_size))
+ ->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, const CompilerType &compiler_type,
+ ConstString name, lldb::addr_t address, AddressType address_type,
+ uint32_t addr_byte_size)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0),
+ m_impl(this, address) {
+ m_value.GetScalar() = address;
+ m_data.SetAddressByteSize(addr_byte_size);
+ m_value.GetScalar().GetData(m_data, addr_byte_size);
+ // m_value.SetValueType(Value::eValueTypeHostAddress);
+ switch (address_type) {
+ case eAddressTypeInvalid:
+ m_value.SetValueType(Value::eValueTypeScalar);
+ break;
+ case eAddressTypeFile:
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ break;
+ case eAddressTypeLoad:
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ break;
+ case eAddressTypeHost:
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ break;
+ }
+ // m_value.SetContext(Value::eContextTypeClangType, compiler_type);
+ m_value.SetCompilerType(compiler_type);
+ m_name = name;
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const Status &error) {
+ return (new ValueObjectConstResult(exe_scope, error))->GetSP();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ const Status &error)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0), m_impl(this) {
+ m_error = error;
+ SetIsConstant();
+}
+
+ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ const Value &value,
+ ConstString name,
+ Module *module)
+ : ValueObject(exe_scope), m_type_name(), m_byte_size(0), m_impl(this) {
+ m_value = value;
+ m_name = name;
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, module);
+}
+
+ValueObjectConstResult::~ValueObjectConstResult() {}
+
+CompilerType ValueObjectConstResult::GetCompilerTypeImpl() {
+ return m_value.GetCompilerType();
+}
+
+lldb::ValueType ValueObjectConstResult::GetValueType() const {
+ return eValueTypeConstResult;
+}
+
+uint64_t ValueObjectConstResult::GetByteSize() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ if (m_byte_size == 0) {
+ if (auto size =
+ GetCompilerType().GetByteSize(exe_ctx.GetBestExecutionContextScope()))
+ SetByteSize(*size);
+ }
+ return m_byte_size;
+}
+
+void ValueObjectConstResult::SetByteSize(size_t size) { m_byte_size = size; }
+
+size_t ValueObjectConstResult::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+ConstString ValueObjectConstResult::GetTypeName() {
+ if (m_type_name.IsEmpty())
+ m_type_name = GetCompilerType().GetConstTypeName();
+ return m_type_name;
+}
+
+ConstString ValueObjectConstResult::GetDisplayTypeName() {
+ return GetCompilerType().GetDisplayTypeName();
+}
+
+bool ValueObjectConstResult::UpdateValue() {
+ // Const value is always valid
+ SetValueIsValid(true);
+ return true;
+}
+
+bool ValueObjectConstResult::IsInScope() {
+ // A const result value is always in scope since it serializes all
+ // information needed to contain the constant value.
+ return true;
+}
+
+lldb::ValueObjectSP ValueObjectConstResult::Dereference(Status &error) {
+ return m_impl.Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObjectConstResult::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ return m_impl.GetSyntheticChildAtOffset(offset, type, can_create,
+ name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResult::AddressOf(Status &error) {
+ return m_impl.AddressOf(error);
+}
+
+lldb::addr_t ValueObjectConstResult::GetAddressOf(bool scalar_is_load_address,
+ AddressType *address_type) {
+ return m_impl.GetAddressOf(scalar_is_load_address, address_type);
+}
+
+ValueObject *ValueObjectConstResult::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ return m_impl.CreateChildAtIndex(idx, synthetic_array_member,
+ synthetic_index);
+}
+
+size_t ValueObjectConstResult::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ return m_impl.GetPointeeData(data, item_idx, item_count);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResult::GetDynamicValue(lldb::DynamicValueType use_dynamic) {
+ // Always recalculate dynamic values for const results as the memory that
+ // they might point to might have changed at any time.
+ if (use_dynamic != eNoDynamicValues) {
+ if (!IsDynamic()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process && process->IsPossibleDynamicValue(*this))
+ m_dynamic_value = new ValueObjectDynamicValue(*this, use_dynamic);
+ }
+ if (m_dynamic_value)
+ return m_dynamic_value->GetSP();
+ }
+ return ValueObjectSP();
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResult::Cast(const CompilerType &compiler_type) {
+ return m_impl.Cast(compiler_type);
+}
+
+lldb::LanguageType ValueObjectConstResult::GetPreferredDisplayLanguage() {
+ if (m_preferred_display_language != lldb::eLanguageTypeUnknown)
+ return m_preferred_display_language;
+ return GetCompilerTypeImpl().GetMinimumLanguage();
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp
new file mode 100644
index 000000000000..b47e699e30f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultCast.cpp
@@ -0,0 +1,62 @@
+//===-- ValueObjectConstResultCast.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResultCast.h"
+
+namespace lldb_private {
+class DataExtractor;
+}
+namespace lldb_private {
+class Status;
+}
+namespace lldb_private {
+class ValueObject;
+}
+
+using namespace lldb_private;
+
+ValueObjectConstResultCast::ValueObjectConstResultCast(
+ ValueObject &parent, ConstString name, const CompilerType &cast_type,
+ lldb::addr_t live_address)
+ : ValueObjectCast(parent, name, cast_type), m_impl(this, live_address) {
+ m_name = name;
+}
+
+ValueObjectConstResultCast::~ValueObjectConstResultCast() {}
+
+lldb::ValueObjectSP ValueObjectConstResultCast::Dereference(Status &error) {
+ return m_impl.Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultCast::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ return m_impl.GetSyntheticChildAtOffset(offset, type, can_create,
+ name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultCast::AddressOf(Status &error) {
+ return m_impl.AddressOf(error);
+}
+
+ValueObject *ValueObjectConstResultCast::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ return m_impl.CreateChildAtIndex(idx, synthetic_array_member,
+ synthetic_index);
+}
+
+size_t ValueObjectConstResultCast::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ return m_impl.GetPointeeData(data, item_idx, item_count);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResultCast::Cast(const CompilerType &compiler_type) {
+ return m_impl.Cast(compiler_type);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp
new file mode 100644
index 000000000000..4e0b303b69d5
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultChild.cpp
@@ -0,0 +1,74 @@
+//===-- ValueObjectConstResultChild.cpp --------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResultChild.h"
+
+#include "lldb/lldb-private-enumerations.h"
+namespace lldb_private {
+class DataExtractor;
+}
+namespace lldb_private {
+class Status;
+}
+namespace lldb_private {
+class ValueObject;
+}
+
+using namespace lldb_private;
+
+ValueObjectConstResultChild::ValueObjectConstResultChild(
+ ValueObject &parent, const CompilerType &compiler_type,
+ ConstString name, uint32_t byte_size, int32_t byte_offset,
+ uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset,
+ bool is_base_class, bool is_deref_of_parent, lldb::addr_t live_address,
+ uint64_t language_flags)
+ : ValueObjectChild(parent, compiler_type, name, byte_size, byte_offset,
+ bitfield_bit_size, bitfield_bit_offset, is_base_class,
+ is_deref_of_parent, eAddressTypeLoad, language_flags),
+ m_impl(this, live_address) {
+ m_name = name;
+}
+
+ValueObjectConstResultChild::~ValueObjectConstResultChild() {}
+
+lldb::ValueObjectSP ValueObjectConstResultChild::Dereference(Status &error) {
+ return m_impl.Dereference(error);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultChild::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ return m_impl.GetSyntheticChildAtOffset(offset, type, can_create,
+ name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultChild::AddressOf(Status &error) {
+ return m_impl.AddressOf(error);
+}
+
+lldb::addr_t ValueObjectConstResultChild::GetAddressOf(
+ bool scalar_is_load_address, AddressType* address_type) {
+ return m_impl.GetAddressOf(scalar_is_load_address, address_type);
+}
+
+ValueObject *ValueObjectConstResultChild::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ return m_impl.CreateChildAtIndex(idx, synthetic_array_member,
+ synthetic_index);
+}
+
+size_t ValueObjectConstResultChild::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ return m_impl.GetPointeeData(data, item_idx, item_count);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResultChild::Cast(const CompilerType &compiler_type) {
+ return m_impl.Cast(compiler_type);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp
new file mode 100644
index 000000000000..de51735736b9
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectConstResultImpl.cpp
@@ -0,0 +1,180 @@
+//===-- ValueObjectConstResultImpl.cpp ---------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectConstResultImpl.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Core/ValueObjectConstResultCast.h"
+#include "lldb/Core/ValueObjectConstResultChild.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/SharingPtr.h"
+
+#include <string>
+
+namespace lldb_private {
+class DataExtractor;
+}
+namespace lldb_private {
+class Status;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectConstResultImpl::ValueObjectConstResultImpl(
+ ValueObject *valobj, lldb::addr_t live_address)
+ : m_impl_backend(valobj), m_live_address(live_address),
+ m_live_address_type(eAddressTypeLoad), m_load_addr_backend(),
+ m_address_of_backend() {}
+
+lldb::ValueObjectSP ValueObjectConstResultImpl::Dereference(Status &error) {
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+
+ return m_impl_backend->ValueObject::Dereference(error);
+}
+
+ValueObject *ValueObjectConstResultImpl::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ if (m_impl_backend == nullptr)
+ return nullptr;
+
+ m_impl_backend->UpdateValueIfNeeded(false);
+
+ ValueObjectConstResultChild *valobj = nullptr;
+
+ bool omit_empty_base_classes = true;
+ bool ignore_array_bounds = synthetic_array_member;
+ std::string child_name_str;
+ uint32_t child_byte_size = 0;
+ int32_t child_byte_offset = 0;
+ uint32_t child_bitfield_bit_size = 0;
+ uint32_t child_bitfield_bit_offset = 0;
+ bool child_is_base_class = false;
+ bool child_is_deref_of_parent = false;
+ uint64_t language_flags;
+
+ const bool transparent_pointers = !synthetic_array_member;
+ CompilerType compiler_type = m_impl_backend->GetCompilerType();
+ CompilerType child_compiler_type;
+
+ ExecutionContext exe_ctx(m_impl_backend->GetExecutionContextRef());
+
+ child_compiler_type = compiler_type.GetChildCompilerTypeAtIndex(
+ &exe_ctx, idx, transparent_pointers, omit_empty_base_classes,
+ ignore_array_bounds, child_name_str, child_byte_size, child_byte_offset,
+ child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class,
+ child_is_deref_of_parent, m_impl_backend, language_flags);
+
+ // One might think we should check that the size of the children
+ // is always strictly positive, hence we could avoid creating a
+ // ValueObject if that's not the case, but it turns out there
+ // are languages out there which allow zero-size types with
+ // children (e.g. Swift).
+ if (child_compiler_type) {
+ if (synthetic_index)
+ child_byte_offset += child_byte_size * synthetic_index;
+
+ ConstString child_name;
+ if (!child_name_str.empty())
+ child_name.SetCString(child_name_str.c_str());
+
+ valobj = new ValueObjectConstResultChild(
+ *m_impl_backend, child_compiler_type, child_name, child_byte_size,
+ child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
+ child_is_base_class, child_is_deref_of_parent,
+ m_live_address == LLDB_INVALID_ADDRESS
+ ? m_live_address
+ : m_live_address + child_byte_offset,
+ language_flags);
+ }
+
+ return valobj;
+}
+
+lldb::ValueObjectSP ValueObjectConstResultImpl::GetSyntheticChildAtOffset(
+ uint32_t offset, const CompilerType &type, bool can_create,
+ ConstString name_const_str) {
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+
+ return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(
+ offset, type, can_create, name_const_str);
+}
+
+lldb::ValueObjectSP ValueObjectConstResultImpl::AddressOf(Status &error) {
+ if (m_address_of_backend.get() != nullptr)
+ return m_address_of_backend;
+
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+ if (m_live_address != LLDB_INVALID_ADDRESS) {
+ CompilerType compiler_type(m_impl_backend->GetCompilerType());
+
+ lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(
+ &m_live_address, sizeof(lldb::addr_t)));
+
+ std::string new_name("&");
+ new_name.append(m_impl_backend->GetName().AsCString(""));
+ ExecutionContext exe_ctx(m_impl_backend->GetExecutionContextRef());
+ m_address_of_backend = ValueObjectConstResult::Create(
+ exe_ctx.GetBestExecutionContextScope(), compiler_type.GetPointerType(),
+ ConstString(new_name.c_str()), buffer, endian::InlHostByteOrder(),
+ exe_ctx.GetAddressByteSize());
+
+ m_address_of_backend->GetValue().SetValueType(Value::eValueTypeScalar);
+ m_address_of_backend->GetValue().GetScalar() = m_live_address;
+
+ return m_address_of_backend;
+ } else
+ return m_impl_backend->ValueObject::AddressOf(error);
+}
+
+lldb::ValueObjectSP
+ValueObjectConstResultImpl::Cast(const CompilerType &compiler_type) {
+ if (m_impl_backend == nullptr)
+ return lldb::ValueObjectSP();
+
+ ValueObjectConstResultCast *result_cast =
+ new ValueObjectConstResultCast(*m_impl_backend, m_impl_backend->GetName(),
+ compiler_type, m_live_address);
+ return result_cast->GetSP();
+}
+
+lldb::addr_t
+ValueObjectConstResultImpl::GetAddressOf(bool scalar_is_load_address,
+ AddressType *address_type) {
+
+ if (m_impl_backend == nullptr)
+ return 0;
+
+ if (m_live_address == LLDB_INVALID_ADDRESS) {
+ return m_impl_backend->ValueObject::GetAddressOf(scalar_is_load_address,
+ address_type);
+ }
+
+ if (address_type)
+ *address_type = m_live_address_type;
+
+ return m_live_address;
+}
+
+size_t ValueObjectConstResultImpl::GetPointeeData(DataExtractor &data,
+ uint32_t item_idx,
+ uint32_t item_count) {
+ if (m_impl_backend == nullptr)
+ return 0;
+ return m_impl_backend->ValueObject::GetPointeeData(data, item_idx,
+ item_count);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp
new file mode 100644
index 000000000000..59037e2b6b25
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectDynamicValue.cpp
@@ -0,0 +1,389 @@
+//===-- ValueObjectDynamicValue.cpp ------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectDynamicValue.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-types.h"
+
+#include <string.h>
+namespace lldb_private {
+class Declaration;
+}
+
+using namespace lldb_private;
+
+ValueObjectDynamicValue::ValueObjectDynamicValue(
+ ValueObject &parent, lldb::DynamicValueType use_dynamic)
+ : ValueObject(parent), m_address(), m_dynamic_type_info(),
+ m_use_dynamic(use_dynamic) {
+ SetName(parent.GetName());
+}
+
+ValueObjectDynamicValue::~ValueObjectDynamicValue() {
+ m_owning_valobj_sp.reset();
+}
+
+CompilerType ValueObjectDynamicValue::GetCompilerTypeImpl() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasType())
+ return m_value.GetCompilerType();
+ else
+ return m_parent->GetCompilerType();
+ }
+ return m_parent->GetCompilerType();
+}
+
+ConstString ValueObjectDynamicValue::GetTypeName() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasName())
+ return m_dynamic_type_info.GetName();
+ }
+ return m_parent->GetTypeName();
+}
+
+TypeImpl ValueObjectDynamicValue::GetTypeImpl() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success && m_type_impl.IsValid()) {
+ return m_type_impl;
+ }
+ return m_parent->GetTypeImpl();
+}
+
+ConstString ValueObjectDynamicValue::GetQualifiedTypeName() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasName())
+ return m_dynamic_type_info.GetName();
+ }
+ return m_parent->GetQualifiedTypeName();
+}
+
+ConstString ValueObjectDynamicValue::GetDisplayTypeName() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success) {
+ if (m_dynamic_type_info.HasType())
+ return GetCompilerType().GetDisplayTypeName();
+ if (m_dynamic_type_info.HasName())
+ return m_dynamic_type_info.GetName();
+ }
+ return m_parent->GetDisplayTypeName();
+}
+
+size_t ValueObjectDynamicValue::CalculateNumChildren(uint32_t max) {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success && m_dynamic_type_info.HasType()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+ } else
+ return m_parent->GetNumChildren(max);
+}
+
+uint64_t ValueObjectDynamicValue::GetByteSize() {
+ const bool success = UpdateValueIfNeeded(false);
+ if (success && m_dynamic_type_info.HasType()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ return m_value.GetValueByteSize(nullptr, &exe_ctx);
+ } else
+ return m_parent->GetByteSize();
+}
+
+lldb::ValueType ValueObjectDynamicValue::GetValueType() const {
+ return m_parent->GetValueType();
+}
+
+bool ValueObjectDynamicValue::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ if (!m_parent->UpdateValueIfNeeded(false)) {
+ // The dynamic value failed to get an error, pass the error along
+ if (m_error.Success() && m_parent->GetError().Fail())
+ m_error = m_parent->GetError();
+ return false;
+ }
+
+ // Setting our type_sp to NULL will route everything back through our parent
+ // which is equivalent to not using dynamic values.
+ if (m_use_dynamic == lldb::eNoDynamicValues) {
+ m_dynamic_type_info.Clear();
+ return true;
+ }
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ }
+
+ // First make sure our Type and/or Address haven't changed:
+ Process *process = exe_ctx.GetProcessPtr();
+ if (!process)
+ return false;
+
+ TypeAndOrName class_type_or_name;
+ Address dynamic_address;
+ bool found_dynamic_type = false;
+ Value::ValueType value_type;
+
+ LanguageRuntime *runtime = nullptr;
+
+ lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage();
+ if (known_type != lldb::eLanguageTypeUnknown &&
+ known_type != lldb::eLanguageTypeC) {
+ runtime = process->GetLanguageRuntime(known_type);
+ if (runtime)
+ found_dynamic_type = runtime->GetDynamicTypeAndAddress(
+ *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
+ value_type);
+ } else {
+ runtime = process->GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus);
+ if (runtime)
+ found_dynamic_type = runtime->GetDynamicTypeAndAddress(
+ *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
+ value_type);
+
+ if (!found_dynamic_type) {
+ runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC);
+ if (runtime)
+ found_dynamic_type = runtime->GetDynamicTypeAndAddress(
+ *m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
+ value_type);
+ }
+ }
+
+ // Getting the dynamic value may have run the program a bit, and so marked us
+ // as needing updating, but we really don't...
+
+ m_update_point.SetUpdated();
+
+ if (runtime && found_dynamic_type) {
+ if (class_type_or_name.HasType()) {
+ m_type_impl =
+ TypeImpl(m_parent->GetCompilerType(),
+ runtime->FixUpDynamicType(class_type_or_name, *m_parent)
+ .GetCompilerType());
+ } else {
+ m_type_impl.Clear();
+ }
+ } else {
+ m_type_impl.Clear();
+ }
+
+ // If we don't have a dynamic type, then make ourselves just a echo of our
+ // parent. Or we could return false, and make ourselves an echo of our
+ // parent?
+ if (!found_dynamic_type) {
+ if (m_dynamic_type_info)
+ SetValueDidChange(true);
+ ClearDynamicTypeInformation();
+ m_dynamic_type_info.Clear();
+ m_value = m_parent->GetValue();
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ return m_error.Success();
+ }
+
+ Value old_value(m_value);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES));
+
+ bool has_changed_type = false;
+
+ if (!m_dynamic_type_info) {
+ m_dynamic_type_info = class_type_or_name;
+ has_changed_type = true;
+ } else if (class_type_or_name != m_dynamic_type_info) {
+ // We are another type, we need to tear down our children...
+ m_dynamic_type_info = class_type_or_name;
+ SetValueDidChange(true);
+ has_changed_type = true;
+ }
+
+ if (has_changed_type)
+ ClearDynamicTypeInformation();
+
+ if (!m_address.IsValid() || m_address != dynamic_address) {
+ if (m_address.IsValid())
+ SetValueDidChange(true);
+
+ // We've moved, so we should be fine...
+ m_address = dynamic_address;
+ lldb::TargetSP target_sp(GetTargetSP());
+ lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
+ m_value.GetScalar() = load_address;
+ }
+
+ if (runtime)
+ m_dynamic_type_info =
+ runtime->FixUpDynamicType(m_dynamic_type_info, *m_parent);
+
+ // m_value.SetContext (Value::eContextTypeClangType, corrected_type);
+ m_value.SetCompilerType(m_dynamic_type_info.GetCompilerType());
+
+ m_value.SetValueType(value_type);
+
+ if (has_changed_type && log)
+ LLDB_LOGF(log, "[%s %p] has a new dynamic type %s", GetName().GetCString(),
+ static_cast<void *>(this), GetTypeName().GetCString());
+
+ if (m_address.IsValid() && m_dynamic_type_info) {
+ // The variable value is in the Scalar value inside the m_value. We can
+ // point our m_data right to it.
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ if (m_error.Success()) {
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(m_value.GetValueType() != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ }
+
+ SetValueIsValid(true);
+ return true;
+ }
+ }
+
+ // We get here if we've failed above...
+ SetValueIsValid(false);
+ return false;
+}
+
+bool ValueObjectDynamicValue::IsInScope() { return m_parent->IsInScope(); }
+
+bool ValueObjectDynamicValue::SetValueFromCString(const char *value_str,
+ Status &error) {
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t my_value = GetValueAsUnsigned(UINT64_MAX);
+ uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX);
+
+ if (my_value == UINT64_MAX || parent_value == UINT64_MAX) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ // if we are at an offset from our parent, in order to set ourselves
+ // correctly we would need to change the new value so that it refers to the
+ // correct dynamic type. we choose not to deal with that - if anything more
+ // than a value overwrite is required, you should be using the expression
+ // parser instead of the value editing facility
+ if (my_value != parent_value) {
+ // but NULL'ing out a value should always be allowed
+ if (strcmp(value_str, "0")) {
+ error.SetErrorString(
+ "unable to modify dynamic value, use 'expression' command");
+ return false;
+ }
+ }
+
+ bool ret_val = m_parent->SetValueFromCString(value_str, error);
+ SetNeedsUpdate();
+ return ret_val;
+}
+
+bool ValueObjectDynamicValue::SetData(DataExtractor &data, Status &error) {
+ if (!UpdateValueIfNeeded(false)) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ uint64_t my_value = GetValueAsUnsigned(UINT64_MAX);
+ uint64_t parent_value = m_parent->GetValueAsUnsigned(UINT64_MAX);
+
+ if (my_value == UINT64_MAX || parent_value == UINT64_MAX) {
+ error.SetErrorString("unable to read value");
+ return false;
+ }
+
+ // if we are at an offset from our parent, in order to set ourselves
+ // correctly we would need to change the new value so that it refers to the
+ // correct dynamic type. we choose not to deal with that - if anything more
+ // than a value overwrite is required, you should be using the expression
+ // parser instead of the value editing facility
+ if (my_value != parent_value) {
+ // but NULL'ing out a value should always be allowed
+ lldb::offset_t offset = 0;
+
+ if (data.GetPointer(&offset) != 0) {
+ error.SetErrorString(
+ "unable to modify dynamic value, use 'expression' command");
+ return false;
+ }
+ }
+
+ bool ret_val = m_parent->SetData(data, error);
+ SetNeedsUpdate();
+ return ret_val;
+}
+
+void ValueObjectDynamicValue::SetPreferredDisplayLanguage(
+ lldb::LanguageType lang) {
+ this->ValueObject::SetPreferredDisplayLanguage(lang);
+ if (m_parent)
+ m_parent->SetPreferredDisplayLanguage(lang);
+}
+
+lldb::LanguageType ValueObjectDynamicValue::GetPreferredDisplayLanguage() {
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
+ if (m_parent)
+ return m_parent->GetPreferredDisplayLanguage();
+ return lldb::eLanguageTypeUnknown;
+ } else
+ return m_preferred_display_language;
+}
+
+bool ValueObjectDynamicValue::IsSyntheticChildrenGenerated() {
+ if (m_parent)
+ return m_parent->IsSyntheticChildrenGenerated();
+ return false;
+}
+
+void ValueObjectDynamicValue::SetSyntheticChildrenGenerated(bool b) {
+ if (m_parent)
+ m_parent->SetSyntheticChildrenGenerated(b);
+ this->ValueObject::SetSyntheticChildrenGenerated(b);
+}
+
+bool ValueObjectDynamicValue::GetDeclaration(Declaration &decl) {
+ if (m_parent)
+ return m_parent->GetDeclaration(decl);
+
+ return ValueObject::GetDeclaration(decl);
+}
+
+uint64_t ValueObjectDynamicValue::GetLanguageFlags() {
+ if (m_parent)
+ return m_parent->GetLanguageFlags();
+ return this->ValueObject::GetLanguageFlags();
+}
+
+void ValueObjectDynamicValue::SetLanguageFlags(uint64_t flags) {
+ if (m_parent)
+ m_parent->SetLanguageFlags(flags);
+ else
+ this->ValueObject::SetLanguageFlags(flags);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp
new file mode 100644
index 000000000000..358a1b14517b
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectList.cpp
@@ -0,0 +1,110 @@
+//===-- ValueObjectList.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectList.h"
+
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/SharingPtr.h"
+
+#include <utility>
+
+using namespace lldb;
+using namespace lldb_private;
+
+const ValueObjectList &ValueObjectList::operator=(const ValueObjectList &rhs) {
+ if (this != &rhs)
+ m_value_objects = rhs.m_value_objects;
+ return *this;
+}
+
+void ValueObjectList::Append(const ValueObjectSP &val_obj_sp) {
+ m_value_objects.push_back(val_obj_sp);
+}
+
+void ValueObjectList::Append(const ValueObjectList &valobj_list) {
+ std::copy(valobj_list.m_value_objects.begin(), // source begin
+ valobj_list.m_value_objects.end(), // source end
+ back_inserter(m_value_objects)); // destination
+}
+
+size_t ValueObjectList::GetSize() const { return m_value_objects.size(); }
+
+void ValueObjectList::Resize(size_t size) { m_value_objects.resize(size); }
+
+lldb::ValueObjectSP ValueObjectList::GetValueObjectAtIndex(size_t idx) {
+ lldb::ValueObjectSP valobj_sp;
+ if (idx < m_value_objects.size())
+ valobj_sp = m_value_objects[idx];
+ return valobj_sp;
+}
+
+lldb::ValueObjectSP ValueObjectList::RemoveValueObjectAtIndex(size_t idx) {
+ lldb::ValueObjectSP valobj_sp;
+ if (idx < m_value_objects.size()) {
+ valobj_sp = m_value_objects[idx];
+ m_value_objects.erase(m_value_objects.begin() + idx);
+ }
+ return valobj_sp;
+}
+
+void ValueObjectList::SetValueObjectAtIndex(size_t idx,
+ const ValueObjectSP &valobj_sp) {
+ if (idx >= m_value_objects.size())
+ m_value_objects.resize(idx + 1);
+ m_value_objects[idx] = valobj_sp;
+}
+
+ValueObjectSP ValueObjectList::FindValueObjectByValueName(const char *name) {
+ ConstString name_const_str(name);
+ ValueObjectSP val_obj_sp;
+ collection::iterator pos, end = m_value_objects.end();
+ for (pos = m_value_objects.begin(); pos != end; ++pos) {
+ ValueObject *valobj = (*pos).get();
+ if (valobj && valobj->GetName() == name_const_str) {
+ val_obj_sp = *pos;
+ break;
+ }
+ }
+ return val_obj_sp;
+}
+
+ValueObjectSP ValueObjectList::FindValueObjectByUID(lldb::user_id_t uid) {
+ ValueObjectSP valobj_sp;
+ collection::iterator pos, end = m_value_objects.end();
+
+ for (pos = m_value_objects.begin(); pos != end; ++pos) {
+ // Watch out for NULL objects in our list as the list might get resized to
+ // a specific size and lazily filled in
+ ValueObject *valobj = (*pos).get();
+ if (valobj && valobj->GetID() == uid) {
+ valobj_sp = *pos;
+ break;
+ }
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP
+ValueObjectList::FindValueObjectByPointer(ValueObject *find_valobj) {
+ ValueObjectSP valobj_sp;
+ collection::iterator pos, end = m_value_objects.end();
+
+ for (pos = m_value_objects.begin(); pos != end; ++pos) {
+ ValueObject *valobj = (*pos).get();
+ if (valobj && valobj == find_valobj) {
+ valobj_sp = *pos;
+ break;
+ }
+ }
+ return valobj_sp;
+}
+
+void ValueObjectList::Swap(ValueObjectList &value_object_list) {
+ m_value_objects.swap(value_object_list.m_value_objects);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp
new file mode 100644
index 000000000000..1a316bf3e7b0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectMemory.cpp
@@ -0,0 +1,228 @@
+//===-- ValueObjectMemory.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-types.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <assert.h>
+#include <memory>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ lldb::TypeSP &type_sp) {
+ return (new ValueObjectMemory(exe_scope, name, address, type_sp))->GetSP();
+}
+
+ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ const CompilerType &ast_type) {
+ return (new ValueObjectMemory(exe_scope, name, address, ast_type))->GetSP();
+}
+
+ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ lldb::TypeSP &type_sp)
+ : ValueObject(exe_scope), m_address(address), m_type_sp(type_sp),
+ m_compiler_type() {
+ // Do not attempt to construct one of these objects with no variable!
+ assert(m_type_sp.get() != nullptr);
+ SetName(ConstString(name));
+ m_value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get());
+ TargetSP target_sp(GetTargetSP());
+ lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
+ if (load_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ m_value.GetScalar() = load_address;
+ } else {
+ lldb::addr_t file_address = m_address.GetFileAddress();
+ if (file_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ m_value.GetScalar() = file_address;
+ } else {
+ m_value.GetScalar() = m_address.GetOffset();
+ m_value.SetValueType(Value::eValueTypeScalar);
+ }
+ }
+}
+
+ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope,
+ llvm::StringRef name,
+ const Address &address,
+ const CompilerType &ast_type)
+ : ValueObject(exe_scope), m_address(address), m_type_sp(),
+ m_compiler_type(ast_type) {
+ // Do not attempt to construct one of these objects with no variable!
+ assert(m_compiler_type.GetTypeSystem());
+ assert(m_compiler_type.GetOpaqueQualType());
+
+ TargetSP target_sp(GetTargetSP());
+
+ SetName(ConstString(name));
+ // m_value.SetContext(Value::eContextTypeClangType,
+ // m_compiler_type.GetOpaqueQualType());
+ m_value.SetCompilerType(m_compiler_type);
+ lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
+ if (load_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ m_value.GetScalar() = load_address;
+ } else {
+ lldb::addr_t file_address = m_address.GetFileAddress();
+ if (file_address != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeFileAddress);
+ m_value.GetScalar() = file_address;
+ } else {
+ m_value.GetScalar() = m_address.GetOffset();
+ m_value.SetValueType(Value::eValueTypeScalar);
+ }
+ }
+}
+
+ValueObjectMemory::~ValueObjectMemory() {}
+
+CompilerType ValueObjectMemory::GetCompilerTypeImpl() {
+ if (m_type_sp)
+ return m_type_sp->GetForwardCompilerType();
+ return m_compiler_type;
+}
+
+ConstString ValueObjectMemory::GetTypeName() {
+ if (m_type_sp)
+ return m_type_sp->GetName();
+ return m_compiler_type.GetConstTypeName();
+}
+
+ConstString ValueObjectMemory::GetDisplayTypeName() {
+ if (m_type_sp)
+ return m_type_sp->GetForwardCompilerType().GetDisplayTypeName();
+ return m_compiler_type.GetDisplayTypeName();
+}
+
+size_t ValueObjectMemory::CalculateNumChildren(uint32_t max) {
+ if (m_type_sp) {
+ auto child_count = m_type_sp->GetNumChildren(true);
+ return child_count <= max ? child_count : max;
+ }
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ const bool omit_empty_base_classes = true;
+ auto child_count =
+ m_compiler_type.GetNumChildren(omit_empty_base_classes, &exe_ctx);
+ return child_count <= max ? child_count : max;
+}
+
+uint64_t ValueObjectMemory::GetByteSize() {
+ if (m_type_sp)
+ return m_type_sp->GetByteSize().getValueOr(0);
+ return m_compiler_type.GetByteSize(nullptr).getValueOr(0);
+}
+
+lldb::ValueType ValueObjectMemory::GetValueType() const {
+ // RETHINK: Should this be inherited from somewhere?
+ return lldb::eValueTypeVariableGlobal;
+}
+
+bool ValueObjectMemory::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ }
+
+ Value old_value(m_value);
+ if (m_address.IsValid()) {
+ Value::ValueType value_type = m_value.GetValueType();
+
+ switch (value_type) {
+ default:
+ llvm_unreachable("Unhandled expression result value kind...");
+
+ case Value::eValueTypeScalar:
+ // The variable value is in the Scalar value inside the m_value. We can
+ // point our m_data right to it.
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ break;
+
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeHostAddress:
+ // The DWARF expression result was an address in the inferior process. If
+ // this variable is an aggregate type, we just need the address as the
+ // main value as all child variable objects will rely upon this location
+ // and add an offset and then read their own values as needed. If this
+ // variable is a simple type, we read all data for it into m_data. Make
+ // sure this type has a value before we try and read it
+
+ // If we have a file address, convert it to a load address if we can.
+ if (value_type == Value::eValueTypeFileAddress &&
+ exe_ctx.GetProcessPtr()) {
+ lldb::addr_t load_addr = m_address.GetLoadAddress(target);
+ if (load_addr != LLDB_INVALID_ADDRESS) {
+ m_value.SetValueType(Value::eValueTypeLoadAddress);
+ m_value.GetScalar() = load_addr;
+ }
+ }
+
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(value_type != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ } else {
+ // Copy the Value and set the context to use our Variable so it can
+ // extract read its value into m_data appropriately
+ Value value(m_value);
+ if (m_type_sp)
+ value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get());
+ else {
+ // value.SetContext(Value::eContextTypeClangType,
+ // m_compiler_type.GetOpaqueQualType());
+ value.SetCompilerType(m_compiler_type);
+ }
+
+ m_error = value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ }
+ break;
+ }
+
+ SetValueIsValid(m_error.Success());
+ }
+ return m_error.Success();
+}
+
+bool ValueObjectMemory::IsInScope() {
+ // FIXME: Maybe try to read the memory address, and if that works, then
+ // we are in scope?
+ return true;
+}
+
+lldb::ModuleSP ValueObjectMemory::GetModule() { return m_address.GetModule(); }
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp
new file mode 100644
index 000000000000..7e97df6d2a34
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectRegister.cpp
@@ -0,0 +1,363 @@
+//===-- ValueObjectRegister.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectRegister.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <assert.h>
+#include <memory>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark ValueObjectRegisterContext
+
+ValueObjectRegisterContext::ValueObjectRegisterContext(
+ ValueObject &parent, RegisterContextSP &reg_ctx)
+ : ValueObject(parent), m_reg_ctx_sp(reg_ctx) {
+ assert(reg_ctx);
+ m_name.SetCString("Registers");
+ SetValueIsValid(true);
+}
+
+ValueObjectRegisterContext::~ValueObjectRegisterContext() {}
+
+CompilerType ValueObjectRegisterContext::GetCompilerTypeImpl() {
+ return CompilerType();
+}
+
+ConstString ValueObjectRegisterContext::GetTypeName() { return ConstString(); }
+
+ConstString ValueObjectRegisterContext::GetDisplayTypeName() {
+ return ConstString();
+}
+
+ConstString ValueObjectRegisterContext::GetQualifiedTypeName() {
+ return ConstString();
+}
+
+size_t ValueObjectRegisterContext::CalculateNumChildren(uint32_t max) {
+ auto reg_set_count = m_reg_ctx_sp->GetRegisterSetCount();
+ return reg_set_count <= max ? reg_set_count : max;
+}
+
+uint64_t ValueObjectRegisterContext::GetByteSize() { return 0; }
+
+bool ValueObjectRegisterContext::UpdateValue() {
+ m_error.Clear();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame)
+ m_reg_ctx_sp = frame->GetRegisterContext();
+ else
+ m_reg_ctx_sp.reset();
+
+ if (m_reg_ctx_sp.get() == nullptr) {
+ SetValueIsValid(false);
+ m_error.SetErrorToGenericError();
+ } else
+ SetValueIsValid(true);
+
+ return m_error.Success();
+}
+
+ValueObject *ValueObjectRegisterContext::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ ValueObject *new_valobj = nullptr;
+
+ const size_t num_children = GetNumChildren();
+ if (idx < num_children) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ new_valobj = new ValueObjectRegisterSet(
+ exe_ctx.GetBestExecutionContextScope(), m_reg_ctx_sp, idx);
+ }
+
+ return new_valobj;
+}
+
+#pragma mark -
+#pragma mark ValueObjectRegisterSet
+
+ValueObjectSP
+ValueObjectRegisterSet::Create(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx_sp,
+ uint32_t set_idx) {
+ return (new ValueObjectRegisterSet(exe_scope, reg_ctx_sp, set_idx))->GetSP();
+}
+
+ValueObjectRegisterSet::ValueObjectRegisterSet(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx,
+ uint32_t reg_set_idx)
+ : ValueObject(exe_scope), m_reg_ctx_sp(reg_ctx), m_reg_set(nullptr),
+ m_reg_set_idx(reg_set_idx) {
+ assert(reg_ctx);
+ m_reg_set = reg_ctx->GetRegisterSet(m_reg_set_idx);
+ if (m_reg_set) {
+ m_name.SetCString(m_reg_set->name);
+ }
+}
+
+ValueObjectRegisterSet::~ValueObjectRegisterSet() {}
+
+CompilerType ValueObjectRegisterSet::GetCompilerTypeImpl() {
+ return CompilerType();
+}
+
+ConstString ValueObjectRegisterSet::GetTypeName() { return ConstString(); }
+
+ConstString ValueObjectRegisterSet::GetQualifiedTypeName() {
+ return ConstString();
+}
+
+size_t ValueObjectRegisterSet::CalculateNumChildren(uint32_t max) {
+ const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx);
+ if (reg_set) {
+ auto reg_count = reg_set->num_registers;
+ return reg_count <= max ? reg_count : max;
+ }
+ return 0;
+}
+
+uint64_t ValueObjectRegisterSet::GetByteSize() { return 0; }
+
+bool ValueObjectRegisterSet::UpdateValue() {
+ m_error.Clear();
+ SetValueDidChange(false);
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame == nullptr)
+ m_reg_ctx_sp.reset();
+ else {
+ m_reg_ctx_sp = frame->GetRegisterContext();
+ if (m_reg_ctx_sp) {
+ const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx);
+ if (reg_set == nullptr)
+ m_reg_ctx_sp.reset();
+ else if (m_reg_set != reg_set) {
+ SetValueDidChange(true);
+ m_name.SetCString(reg_set->name);
+ }
+ }
+ }
+ if (m_reg_ctx_sp) {
+ SetValueIsValid(true);
+ } else {
+ SetValueIsValid(false);
+ m_error.SetErrorToGenericError();
+ m_children.Clear();
+ }
+ return m_error.Success();
+}
+
+ValueObject *ValueObjectRegisterSet::CreateChildAtIndex(
+ size_t idx, bool synthetic_array_member, int32_t synthetic_index) {
+ ValueObject *valobj = nullptr;
+ if (m_reg_ctx_sp && m_reg_set) {
+ const size_t num_children = GetNumChildren();
+ if (idx < num_children)
+ valobj = new ValueObjectRegister(*this, m_reg_ctx_sp,
+ m_reg_set->registers[idx]);
+ }
+ return valobj;
+}
+
+lldb::ValueObjectSP
+ValueObjectRegisterSet::GetChildMemberWithName(ConstString name,
+ bool can_create) {
+ ValueObject *valobj = nullptr;
+ if (m_reg_ctx_sp && m_reg_set) {
+ const RegisterInfo *reg_info =
+ m_reg_ctx_sp->GetRegisterInfoByName(name.AsCString());
+ if (reg_info != nullptr)
+ valobj = new ValueObjectRegister(*this, m_reg_ctx_sp,
+ reg_info->kinds[eRegisterKindLLDB]);
+ }
+ if (valobj)
+ return valobj->GetSP();
+ else
+ return ValueObjectSP();
+}
+
+size_t
+ValueObjectRegisterSet::GetIndexOfChildWithName(ConstString name) {
+ if (m_reg_ctx_sp && m_reg_set) {
+ const RegisterInfo *reg_info =
+ m_reg_ctx_sp->GetRegisterInfoByName(name.AsCString());
+ if (reg_info != nullptr)
+ return reg_info->kinds[eRegisterKindLLDB];
+ }
+ return UINT32_MAX;
+}
+
+#pragma mark -
+#pragma mark ValueObjectRegister
+
+void ValueObjectRegister::ConstructObject(uint32_t reg_num) {
+ const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoAtIndex(reg_num);
+ if (reg_info) {
+ m_reg_info = *reg_info;
+ if (reg_info->name)
+ m_name.SetCString(reg_info->name);
+ else if (reg_info->alt_name)
+ m_name.SetCString(reg_info->alt_name);
+ }
+}
+
+ValueObjectRegister::ValueObjectRegister(ValueObject &parent,
+ lldb::RegisterContextSP &reg_ctx_sp,
+ uint32_t reg_num)
+ : ValueObject(parent), m_reg_ctx_sp(reg_ctx_sp), m_reg_info(),
+ m_reg_value(), m_type_name(), m_compiler_type() {
+ assert(reg_ctx_sp.get());
+ ConstructObject(reg_num);
+}
+
+ValueObjectSP ValueObjectRegister::Create(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx_sp,
+ uint32_t reg_num) {
+ return (new ValueObjectRegister(exe_scope, reg_ctx_sp, reg_num))->GetSP();
+}
+
+ValueObjectRegister::ValueObjectRegister(ExecutionContextScope *exe_scope,
+ lldb::RegisterContextSP &reg_ctx,
+ uint32_t reg_num)
+ : ValueObject(exe_scope), m_reg_ctx_sp(reg_ctx), m_reg_info(),
+ m_reg_value(), m_type_name(), m_compiler_type() {
+ assert(reg_ctx);
+ ConstructObject(reg_num);
+}
+
+ValueObjectRegister::~ValueObjectRegister() {}
+
+CompilerType ValueObjectRegister::GetCompilerTypeImpl() {
+ if (!m_compiler_type.IsValid()) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ if (auto *target = exe_ctx.GetTargetPtr()) {
+ if (auto *exe_module = target->GetExecutableModulePointer()) {
+ auto type_system_or_err =
+ exe_module->GetTypeSystemForLanguage(eLanguageTypeC);
+ if (auto err = type_system_or_err.takeError()) {
+ LLDB_LOG_ERROR(
+ lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TYPES),
+ std::move(err), "Unable to get CompilerType from TypeSystem");
+ } else {
+ m_compiler_type =
+ type_system_or_err->GetBuiltinTypeForEncodingAndBitSize(
+ m_reg_info.encoding, m_reg_info.byte_size * 8);
+ }
+ }
+ }
+ }
+ return m_compiler_type;
+}
+
+ConstString ValueObjectRegister::GetTypeName() {
+ if (m_type_name.IsEmpty())
+ m_type_name = GetCompilerType().GetConstTypeName();
+ return m_type_name;
+}
+
+size_t ValueObjectRegister::CalculateNumChildren(uint32_t max) {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx);
+ return children_count <= max ? children_count : max;
+}
+
+uint64_t ValueObjectRegister::GetByteSize() { return m_reg_info.byte_size; }
+
+bool ValueObjectRegister::UpdateValue() {
+ m_error.Clear();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame == nullptr) {
+ m_reg_ctx_sp.reset();
+ m_reg_value.Clear();
+ }
+
+ if (m_reg_ctx_sp) {
+ RegisterValue m_old_reg_value(m_reg_value);
+ if (m_reg_ctx_sp->ReadRegister(&m_reg_info, m_reg_value)) {
+ if (m_reg_value.GetData(m_data)) {
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process)
+ m_data.SetAddressByteSize(process->GetAddressByteSize());
+ m_value.SetContext(Value::eContextTypeRegisterInfo,
+ (void *)&m_reg_info);
+ m_value.SetValueType(Value::eValueTypeHostAddress);
+ m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
+ SetValueIsValid(true);
+ SetValueDidChange(!(m_old_reg_value == m_reg_value));
+ return true;
+ }
+ }
+ }
+
+ SetValueIsValid(false);
+ m_error.SetErrorToGenericError();
+ return false;
+}
+
+bool ValueObjectRegister::SetValueFromCString(const char *value_str,
+ Status &error) {
+ // The new value will be in the m_data. Copy that into our register value.
+ error =
+ m_reg_value.SetValueFromString(&m_reg_info, llvm::StringRef(value_str));
+ if (error.Success()) {
+ if (m_reg_ctx_sp->WriteRegister(&m_reg_info, m_reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else
+ return false;
+ } else
+ return false;
+}
+
+bool ValueObjectRegister::SetData(DataExtractor &data, Status &error) {
+ error = m_reg_value.SetValueFromData(&m_reg_info, data, 0, false);
+ if (error.Success()) {
+ if (m_reg_ctx_sp->WriteRegister(&m_reg_info, m_reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else
+ return false;
+ } else
+ return false;
+}
+
+bool ValueObjectRegister::ResolveValue(Scalar &scalar) {
+ if (UpdateValueIfNeeded(
+ false)) // make sure that you are up to date before returning anything
+ return m_reg_value.GetScalarValue(scalar);
+ return false;
+}
+
+void ValueObjectRegister::GetExpressionPath(Stream &s,
+ bool qualify_cxx_base_classes,
+ GetExpressionPathFormat epformat) {
+ s.Printf("$%s", m_reg_info.name);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp
new file mode 100644
index 000000000000..a6bf35eac70a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp
@@ -0,0 +1,394 @@
+//===-- ValueObjectSyntheticFilter.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectSyntheticFilter.h"
+
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+#include "lldb/Utility/SharingPtr.h"
+#include "lldb/Utility/Status.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+namespace lldb_private {
+class Declaration;
+}
+
+using namespace lldb_private;
+
+class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ DummySyntheticFrontEnd(ValueObject &backend)
+ : SyntheticChildrenFrontEnd(backend) {}
+
+ size_t CalculateNumChildren() override { return m_backend.GetNumChildren(); }
+
+ lldb::ValueObjectSP GetChildAtIndex(size_t idx) override {
+ return m_backend.GetChildAtIndex(idx, true);
+ }
+
+ size_t GetIndexOfChildWithName(ConstString name) override {
+ return m_backend.GetIndexOfChildWithName(name);
+ }
+
+ bool MightHaveChildren() override { return true; }
+
+ bool Update() override { return false; }
+};
+
+ValueObjectSynthetic::ValueObjectSynthetic(ValueObject &parent,
+ lldb::SyntheticChildrenSP filter)
+ : ValueObject(parent), m_synth_sp(filter), m_children_byindex(),
+ m_name_toindex(), m_synthetic_children_count(UINT32_MAX),
+ m_synthetic_children_cache(), m_parent_type_name(parent.GetTypeName()),
+ m_might_have_children(eLazyBoolCalculate),
+ m_provides_value(eLazyBoolCalculate) {
+ SetName(parent.GetName());
+ CopyValueData(m_parent);
+ CreateSynthFilter();
+}
+
+ValueObjectSynthetic::~ValueObjectSynthetic() = default;
+
+CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() {
+ return m_parent->GetCompilerType();
+}
+
+ConstString ValueObjectSynthetic::GetTypeName() {
+ return m_parent->GetTypeName();
+}
+
+ConstString ValueObjectSynthetic::GetQualifiedTypeName() {
+ return m_parent->GetQualifiedTypeName();
+}
+
+ConstString ValueObjectSynthetic::GetDisplayTypeName() {
+ if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName())
+ return synth_name;
+
+ return m_parent->GetDisplayTypeName();
+}
+
+size_t ValueObjectSynthetic::CalculateNumChildren(uint32_t max) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
+
+ UpdateValueIfNeeded();
+ if (m_synthetic_children_count < UINT32_MAX)
+ return m_synthetic_children_count <= max ? m_synthetic_children_count : max;
+
+ if (max < UINT32_MAX) {
+ size_t num_children = m_synth_filter_up->CalculateNumChildren(max);
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
+ "%s and type %s, the filter returned %zu child values",
+ GetName().AsCString(), GetTypeName().AsCString(), num_children);
+ return num_children;
+ } else {
+ size_t num_children = (m_synthetic_children_count =
+ m_synth_filter_up->CalculateNumChildren(max));
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::CalculateNumChildren] for VO of name "
+ "%s and type %s, the filter returned %zu child values",
+ GetName().AsCString(), GetTypeName().AsCString(), num_children);
+ return num_children;
+ }
+}
+
+lldb::ValueObjectSP
+ValueObjectSynthetic::GetDynamicValue(lldb::DynamicValueType valueType) {
+ if (!m_parent)
+ return lldb::ValueObjectSP();
+ if (IsDynamic() && GetDynamicValueType() == valueType)
+ return GetSP();
+ return m_parent->GetDynamicValue(valueType);
+}
+
+bool ValueObjectSynthetic::MightHaveChildren() {
+ if (m_might_have_children == eLazyBoolCalculate)
+ m_might_have_children =
+ (m_synth_filter_up->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo);
+ return (m_might_have_children != eLazyBoolNo);
+}
+
+uint64_t ValueObjectSynthetic::GetByteSize() { return m_parent->GetByteSize(); }
+
+lldb::ValueType ValueObjectSynthetic::GetValueType() const {
+ return m_parent->GetValueType();
+}
+
+void ValueObjectSynthetic::CreateSynthFilter() {
+ ValueObject *valobj_for_frontend = m_parent;
+ if (m_synth_sp->WantsDereference())
+ {
+ CompilerType type = m_parent->GetCompilerType();
+ if (type.IsValid() && type.IsPointerOrReferenceType())
+ {
+ Status error;
+ lldb::ValueObjectSP deref_sp = m_parent->Dereference(error);
+ if (error.Success())
+ valobj_for_frontend = deref_sp.get();
+ }
+ }
+ m_synth_filter_up = (m_synth_sp->GetFrontEnd(*valobj_for_frontend));
+ if (!m_synth_filter_up)
+ m_synth_filter_up = std::make_unique<DummySyntheticFrontEnd>(*m_parent);
+}
+
+bool ValueObjectSynthetic::UpdateValue() {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
+
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ if (!m_parent->UpdateValueIfNeeded(false)) {
+ // our parent could not update.. as we are meaningless without a parent,
+ // just stop
+ if (m_parent->GetError().Fail())
+ m_error = m_parent->GetError();
+ return false;
+ }
+
+ // regenerate the synthetic filter if our typename changes
+ // <rdar://problem/12424824>
+ ConstString new_parent_type_name = m_parent->GetTypeName();
+ if (new_parent_type_name != m_parent_type_name) {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::UpdateValue] name=%s, type changed "
+ "from %s to %s, recomputing synthetic filter",
+ GetName().AsCString(), m_parent_type_name.AsCString(),
+ new_parent_type_name.AsCString());
+ m_parent_type_name = new_parent_type_name;
+ CreateSynthFilter();
+ }
+
+ // let our backend do its update
+ if (!m_synth_filter_up->Update()) {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said caches are stale - clearing",
+ GetName().AsCString());
+ // filter said that cached values are stale
+ m_children_byindex.Clear();
+ m_name_toindex.Clear();
+ // usually, an object's value can change but this does not alter its
+ // children count for a synthetic VO that might indeed happen, so we need
+ // to tell the upper echelons that they need to come back to us asking for
+ // children
+ m_children_count_valid = false;
+ m_synthetic_children_cache.Clear();
+ m_synthetic_children_count = UINT32_MAX;
+ m_might_have_children = eLazyBoolCalculate;
+ } else {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said caches are still valid",
+ GetName().AsCString());
+ }
+
+ m_provides_value = eLazyBoolCalculate;
+
+ lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue());
+
+ if (synth_val && synth_val->CanProvideValue()) {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said it can provide a value",
+ GetName().AsCString());
+
+ m_provides_value = eLazyBoolYes;
+ CopyValueData(synth_val.get());
+ } else {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic "
+ "filter said it will not provide a value",
+ GetName().AsCString());
+
+ m_provides_value = eLazyBoolNo;
+ CopyValueData(m_parent);
+ }
+
+ SetValueIsValid(true);
+ return true;
+}
+
+lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(size_t idx,
+ bool can_create) {
+ Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS);
+
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving "
+ "child at index %zu",
+ GetName().AsCString(), idx);
+
+ UpdateValueIfNeeded();
+
+ ValueObject *valobj;
+ if (!m_children_byindex.GetValueForKey(idx, valobj)) {
+ if (can_create && m_synth_filter_up != nullptr) {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at "
+ "index %zu not cached and will be created",
+ GetName().AsCString(), idx);
+
+ lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx);
+
+ LLDB_LOGF(
+ log,
+ "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at index "
+ "%zu created as %p (is "
+ "synthetic: %s)",
+ GetName().AsCString(), idx, static_cast<void *>(synth_guy.get()),
+ synth_guy.get()
+ ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no")
+ : "no");
+
+ if (!synth_guy)
+ return synth_guy;
+
+ if (synth_guy->IsSyntheticChildrenGenerated())
+ m_synthetic_children_cache.AppendObject(synth_guy);
+ m_children_byindex.SetValueForKey(idx, synth_guy.get());
+ synth_guy->SetPreferredDisplayLanguageIfNeeded(
+ GetPreferredDisplayLanguage());
+ return synth_guy;
+ } else {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at "
+ "index %zu not cached and cannot "
+ "be created (can_create = %s, synth_filter = %p)",
+ GetName().AsCString(), idx, can_create ? "yes" : "no",
+ static_cast<void *>(m_synth_filter_up.get()));
+
+ return lldb::ValueObjectSP();
+ }
+ } else {
+ LLDB_LOGF(log,
+ "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at "
+ "index %zu cached as %p",
+ GetName().AsCString(), idx, static_cast<void *>(valobj));
+
+ return valobj->GetSP();
+ }
+}
+
+lldb::ValueObjectSP
+ValueObjectSynthetic::GetChildMemberWithName(ConstString name,
+ bool can_create) {
+ UpdateValueIfNeeded();
+
+ uint32_t index = GetIndexOfChildWithName(name);
+
+ if (index == UINT32_MAX)
+ return lldb::ValueObjectSP();
+
+ return GetChildAtIndex(index, can_create);
+}
+
+size_t ValueObjectSynthetic::GetIndexOfChildWithName(ConstString name) {
+ UpdateValueIfNeeded();
+
+ uint32_t found_index = UINT32_MAX;
+ bool did_find = m_name_toindex.GetValueForKey(name.GetCString(), found_index);
+
+ if (!did_find && m_synth_filter_up != nullptr) {
+ uint32_t index = m_synth_filter_up->GetIndexOfChildWithName(name);
+ if (index == UINT32_MAX)
+ return index;
+ m_name_toindex.SetValueForKey(name.GetCString(), index);
+ return index;
+ } else if (!did_find && m_synth_filter_up == nullptr)
+ return UINT32_MAX;
+ else /*if (iter != m_name_toindex.end())*/
+ return found_index;
+}
+
+bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); }
+
+lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() {
+ return m_parent->GetSP();
+}
+
+void ValueObjectSynthetic::CopyValueData(ValueObject *source) {
+ m_value = (source->UpdateValueIfNeeded(), source->GetValue());
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+}
+
+bool ValueObjectSynthetic::CanProvideValue() {
+ if (!UpdateValueIfNeeded())
+ return false;
+ if (m_provides_value == eLazyBoolYes)
+ return true;
+ return m_parent->CanProvideValue();
+}
+
+bool ValueObjectSynthetic::SetValueFromCString(const char *value_str,
+ Status &error) {
+ return m_parent->SetValueFromCString(value_str, error);
+}
+
+void ValueObjectSynthetic::SetFormat(lldb::Format format) {
+ if (m_parent) {
+ m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll);
+ m_parent->SetFormat(format);
+ }
+ this->ValueObject::SetFormat(format);
+ this->ClearUserVisibleData(eClearUserVisibleDataItemsAll);
+}
+
+void ValueObjectSynthetic::SetPreferredDisplayLanguage(
+ lldb::LanguageType lang) {
+ this->ValueObject::SetPreferredDisplayLanguage(lang);
+ if (m_parent)
+ m_parent->SetPreferredDisplayLanguage(lang);
+}
+
+lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() {
+ if (m_preferred_display_language == lldb::eLanguageTypeUnknown) {
+ if (m_parent)
+ return m_parent->GetPreferredDisplayLanguage();
+ return lldb::eLanguageTypeUnknown;
+ } else
+ return m_preferred_display_language;
+}
+
+bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() {
+ if (m_parent)
+ return m_parent->IsSyntheticChildrenGenerated();
+ return false;
+}
+
+void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) {
+ if (m_parent)
+ m_parent->SetSyntheticChildrenGenerated(b);
+ this->ValueObject::SetSyntheticChildrenGenerated(b);
+}
+
+bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) {
+ if (m_parent)
+ return m_parent->GetDeclaration(decl);
+
+ return ValueObject::GetDeclaration(decl);
+}
+
+uint64_t ValueObjectSynthetic::GetLanguageFlags() {
+ if (m_parent)
+ return m_parent->GetLanguageFlags();
+ return this->ValueObject::GetLanguageFlags();
+}
+
+void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) {
+ if (m_parent)
+ m_parent->SetLanguageFlags(flags);
+ else
+ this->ValueObject::SetLanguageFlags(flags);
+}
diff --git a/contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp b/contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp
new file mode 100644
index 000000000000..33f9d5410843
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Core/ValueObjectVariable.cpp
@@ -0,0 +1,376 @@
+//===-- ValueObjectVariable.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/ValueObjectVariable.h"
+
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/Declaration.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolContextScope.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private-enumerations.h"
+#include "lldb/lldb-types.h"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <assert.h>
+#include <memory>
+
+namespace lldb_private {
+class ExecutionContextScope;
+}
+namespace lldb_private {
+class StackFrame;
+}
+namespace lldb_private {
+struct RegisterInfo;
+}
+using namespace lldb_private;
+
+lldb::ValueObjectSP
+ValueObjectVariable::Create(ExecutionContextScope *exe_scope,
+ const lldb::VariableSP &var_sp) {
+ return (new ValueObjectVariable(exe_scope, var_sp))->GetSP();
+}
+
+ValueObjectVariable::ValueObjectVariable(ExecutionContextScope *exe_scope,
+ const lldb::VariableSP &var_sp)
+ : ValueObject(exe_scope), m_variable_sp(var_sp) {
+ // Do not attempt to construct one of these objects with no variable!
+ assert(m_variable_sp.get() != nullptr);
+ m_name = var_sp->GetName();
+}
+
+ValueObjectVariable::~ValueObjectVariable() {}
+
+CompilerType ValueObjectVariable::GetCompilerTypeImpl() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetForwardCompilerType();
+ return CompilerType();
+}
+
+ConstString ValueObjectVariable::GetTypeName() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetName();
+ return ConstString();
+}
+
+ConstString ValueObjectVariable::GetDisplayTypeName() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetForwardCompilerType().GetDisplayTypeName();
+ return ConstString();
+}
+
+ConstString ValueObjectVariable::GetQualifiedTypeName() {
+ Type *var_type = m_variable_sp->GetType();
+ if (var_type)
+ return var_type->GetQualifiedName();
+ return ConstString();
+}
+
+size_t ValueObjectVariable::CalculateNumChildren(uint32_t max) {
+ CompilerType type(GetCompilerType());
+
+ if (!type.IsValid())
+ return 0;
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ const bool omit_empty_base_classes = true;
+ auto child_count = type.GetNumChildren(omit_empty_base_classes, &exe_ctx);
+ return child_count <= max ? child_count : max;
+}
+
+uint64_t ValueObjectVariable::GetByteSize() {
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ CompilerType type(GetCompilerType());
+
+ if (!type.IsValid())
+ return 0;
+
+ return type.GetByteSize(exe_ctx.GetBestExecutionContextScope()).getValueOr(0);
+}
+
+lldb::ValueType ValueObjectVariable::GetValueType() const {
+ if (m_variable_sp)
+ return m_variable_sp->GetScope();
+ return lldb::eValueTypeInvalid;
+}
+
+bool ValueObjectVariable::UpdateValue() {
+ SetValueIsValid(false);
+ m_error.Clear();
+
+ Variable *variable = m_variable_sp.get();
+ DWARFExpression &expr = variable->LocationExpression();
+
+ if (variable->GetLocationIsConstantValueData()) {
+ // expr doesn't contain DWARF bytes, it contains the constant variable
+ // value bytes themselves...
+ if (expr.GetExpressionData(m_data))
+ m_value.SetContext(Value::eContextTypeVariable, variable);
+ else
+ m_error.SetErrorString("empty constant data");
+ // constant bytes can't be edited - sorry
+ m_resolved_value.SetContext(Value::eContextTypeInvalid, nullptr);
+ } else {
+ lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS;
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ m_data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
+ }
+
+ if (expr.IsLocationList()) {
+ SymbolContext sc;
+ variable->CalculateSymbolContext(&sc);
+ if (sc.function)
+ loclist_base_load_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(
+ target);
+ }
+ Value old_value(m_value);
+ if (expr.Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, nullptr,
+ nullptr, m_value, &m_error)) {
+ m_resolved_value = m_value;
+ m_value.SetContext(Value::eContextTypeVariable, variable);
+
+ CompilerType compiler_type = GetCompilerType();
+ if (compiler_type.IsValid())
+ m_value.SetCompilerType(compiler_type);
+
+ Value::ValueType value_type = m_value.GetValueType();
+
+ Process *process = exe_ctx.GetProcessPtr();
+ const bool process_is_alive = process && process->IsAlive();
+ const uint32_t type_info = compiler_type.GetTypeInfo();
+ const bool is_pointer_or_ref =
+ (type_info & (lldb::eTypeIsPointer | lldb::eTypeIsReference)) != 0;
+
+ switch (value_type) {
+ case Value::eValueTypeFileAddress:
+ // If this type is a pointer, then its children will be considered load
+ // addresses if the pointer or reference is dereferenced, but only if
+ // the process is alive.
+ //
+ // There could be global variables like in the following code:
+ // struct LinkedListNode { Foo* foo; LinkedListNode* next; };
+ // Foo g_foo1;
+ // Foo g_foo2;
+ // LinkedListNode g_second_node = { &g_foo2, NULL };
+ // LinkedListNode g_first_node = { &g_foo1, &g_second_node };
+ //
+ // When we aren't running, we should be able to look at these variables
+ // using the "target variable" command. Children of the "g_first_node"
+ // always will be of the same address type as the parent. But children
+ // of the "next" member of LinkedListNode will become load addresses if
+ // we have a live process, or remain what a file address if it what a
+ // file address.
+ if (process_is_alive && is_pointer_or_ref)
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+ else
+ SetAddressTypeOfChildren(eAddressTypeFile);
+ break;
+ case Value::eValueTypeHostAddress:
+ // Same as above for load addresses, except children of pointer or refs
+ // are always load addresses. Host addresses are used to store freeze
+ // dried variables. If this type is a struct, the entire struct
+ // contents will be copied into the heap of the
+ // LLDB process, but we do not currently follow any pointers.
+ if (is_pointer_or_ref)
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+ else
+ SetAddressTypeOfChildren(eAddressTypeHost);
+ break;
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeScalar:
+ case Value::eValueTypeVector:
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+ break;
+ }
+
+ switch (value_type) {
+ case Value::eValueTypeVector:
+ // fall through
+ case Value::eValueTypeScalar:
+ // The variable value is in the Scalar value inside the m_value. We can
+ // point our m_data right to it.
+ m_error =
+ m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+ break;
+
+ case Value::eValueTypeFileAddress:
+ case Value::eValueTypeLoadAddress:
+ case Value::eValueTypeHostAddress:
+ // The DWARF expression result was an address in the inferior process.
+ // If this variable is an aggregate type, we just need the address as
+ // the main value as all child variable objects will rely upon this
+ // location and add an offset and then read their own values as needed.
+ // If this variable is a simple type, we read all data for it into
+ // m_data. Make sure this type has a value before we try and read it
+
+ // If we have a file address, convert it to a load address if we can.
+ if (value_type == Value::eValueTypeFileAddress && process_is_alive)
+ m_value.ConvertToLoadAddress(GetModule().get(), target);
+
+ if (!CanProvideValue()) {
+ // this value object represents an aggregate type whose children have
+ // values, but this object does not. So we say we are changed if our
+ // location has changed.
+ SetValueDidChange(value_type != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ } else {
+ // Copy the Value and set the context to use our Variable so it can
+ // extract read its value into m_data appropriately
+ Value value(m_value);
+ value.SetContext(Value::eContextTypeVariable, variable);
+ m_error =
+ value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
+
+ SetValueDidChange(value_type != old_value.GetValueType() ||
+ m_value.GetScalar() != old_value.GetScalar());
+ }
+ break;
+ }
+
+ SetValueIsValid(m_error.Success());
+ } else {
+ // could not find location, won't allow editing
+ m_resolved_value.SetContext(Value::eContextTypeInvalid, nullptr);
+ }
+ }
+ return m_error.Success();
+}
+
+bool ValueObjectVariable::IsInScope() {
+ const ExecutionContextRef &exe_ctx_ref = GetExecutionContextRef();
+ if (exe_ctx_ref.HasFrameRef()) {
+ ExecutionContext exe_ctx(exe_ctx_ref);
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ return m_variable_sp->IsInScope(frame);
+ } else {
+ // This ValueObject had a frame at one time, but now we can't locate it,
+ // so return false since we probably aren't in scope.
+ return false;
+ }
+ }
+ // We have a variable that wasn't tied to a frame, which means it is a global
+ // and is always in scope.
+ return true;
+}
+
+lldb::ModuleSP ValueObjectVariable::GetModule() {
+ if (m_variable_sp) {
+ SymbolContextScope *sc_scope = m_variable_sp->GetSymbolContextScope();
+ if (sc_scope) {
+ return sc_scope->CalculateSymbolContextModule();
+ }
+ }
+ return lldb::ModuleSP();
+}
+
+SymbolContextScope *ValueObjectVariable::GetSymbolContextScope() {
+ if (m_variable_sp)
+ return m_variable_sp->GetSymbolContextScope();
+ return nullptr;
+}
+
+bool ValueObjectVariable::GetDeclaration(Declaration &decl) {
+ if (m_variable_sp) {
+ decl = m_variable_sp->GetDeclaration();
+ return true;
+ }
+ return false;
+}
+
+const char *ValueObjectVariable::GetLocationAsCString() {
+ if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo)
+ return GetLocationAsCStringImpl(m_resolved_value, m_data);
+ else
+ return ValueObject::GetLocationAsCString();
+}
+
+bool ValueObjectVariable::SetValueFromCString(const char *value_str,
+ Status &error) {
+ if (!UpdateValueIfNeeded()) {
+ error.SetErrorString("unable to update value before writing");
+ return false;
+ }
+
+ if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
+ RegisterValue reg_value;
+ if (!reg_info || !reg_ctx) {
+ error.SetErrorString("unable to retrieve register info");
+ return false;
+ }
+ error = reg_value.SetValueFromString(reg_info, llvm::StringRef(value_str));
+ if (error.Fail())
+ return false;
+ if (reg_ctx->WriteRegister(reg_info, reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else {
+ error.SetErrorString("unable to write back to register");
+ return false;
+ }
+ } else
+ return ValueObject::SetValueFromCString(value_str, error);
+}
+
+bool ValueObjectVariable::SetData(DataExtractor &data, Status &error) {
+ if (!UpdateValueIfNeeded()) {
+ error.SetErrorString("unable to update value before writing");
+ return false;
+ }
+
+ if (m_resolved_value.GetContextType() == Value::eContextTypeRegisterInfo) {
+ RegisterInfo *reg_info = m_resolved_value.GetRegisterInfo();
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
+ RegisterValue reg_value;
+ if (!reg_info || !reg_ctx) {
+ error.SetErrorString("unable to retrieve register info");
+ return false;
+ }
+ error = reg_value.SetValueFromData(reg_info, data, 0, true);
+ if (error.Fail())
+ return false;
+ if (reg_ctx->WriteRegister(reg_info, reg_value)) {
+ SetNeedsUpdate();
+ return true;
+ } else {
+ error.SetErrorString("unable to write back to register");
+ return false;
+ }
+ } else
+ return ValueObject::SetData(data, error);
+}