diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Core')
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 §ion, 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 ®) { + return [®](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 ®_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 ®_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 ®_value) { + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return ReadRegister(®_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 ®_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 ®_value) { + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return WriteRegister(context, ®_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, ®_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 ®_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 ®_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 ®_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 ®_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 ®_kind, + uint32_t ®_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 ®_info) { + lldb::RegisterKind reg_kind; + uint32_t reg_num; + if (reg_ctx && GetBestRegisterKindAndNumber(®_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 ®ex, + 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 ®ex, + 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 ®ex, 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 ®ex, + 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 ®ex, + 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 ®ex, 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 §ion_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 §ion_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 §_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 §_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 ®ex, + 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 ®ex, 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 ®_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 ®_ctx_sp, + uint32_t set_idx) { + return (new ValueObjectRegisterSet(exe_scope, reg_ctx_sp, set_idx))->GetSP(); +} + +ValueObjectRegisterSet::ValueObjectRegisterSet(ExecutionContextScope *exe_scope, + lldb::RegisterContextSP ®_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 ®_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 ®_ctx_sp, + uint32_t reg_num) { + return (new ValueObjectRegister(exe_scope, reg_ctx_sp, reg_num))->GetSP(); +} + +ValueObjectRegister::ValueObjectRegister(ExecutionContextScope *exe_scope, + lldb::RegisterContextSP ®_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); +} |