diff options
author | emaste <emaste@FreeBSD.org> | 2013-08-23 17:46:38 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2013-08-23 17:46:38 +0000 |
commit | dcd15f81789e389c1cb27d264fcdddfd0a6002bd (patch) | |
tree | f561dabc721ad515599172c16da3a4400b7f4aec /source/Core | |
download | FreeBSD-src-dcd15f81789e389c1cb27d264fcdddfd0a6002bd.zip FreeBSD-src-dcd15f81789e389c1cb27d264fcdddfd0a6002bd.tar.gz |
Import lldb as of SVN r188801
(A number of files not required for the FreeBSD build have been removed.)
Sponsored by: DARPA, AFRL
Diffstat (limited to 'source/Core')
70 files changed, 37536 insertions, 0 deletions
diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp new file mode 100644 index 0000000..8d599d8 --- /dev/null +++ b/source/Core/Address.cpp @@ -0,0 +1,1045 @@ +//===-- Address.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "llvm/ADT/Triple.h" + +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 == NULL) + return 0; + + TargetSP target_sp (exe_scope->CalculateTarget()); + if (target_sp) + { + Error 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 == NULL) + 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 == NULL || 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 == NULL) + 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 == NULL || byte_size == 0) + return 0; + 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); + + data.Dump (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 == NULL) + 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), lldb::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; + + data.Dump (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.load(); + } + 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 +} + +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; + } + // 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) + { + // No section, we just return the offset since it is the value in this case + return m_offset; + } + + 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; + } + } + // The section isn't resolved or no process was supplied so we can't + // return a valid file address. + return LLDB_INVALID_ADDRESS; +} + +addr_t +Address::GetCallableLoadAddress (Target *target, bool is_indirect) const +{ + if (is_indirect && target) { + ProcessSP processSP = target->GetProcessSP(); + Error error; + if (processSP.get()) + return processSP->ResolveIndirectFunction(this, error); + } + + addr_t code_addr = GetLoadAddress (target); + + 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) const +{ + addr_t code_addr = GetLoadAddress (target); + if (code_addr != LLDB_INVALID_ADDRESS) + code_addr = target->GetOpcodeLoadAddress (code_addr, GetAddressClass()); + return code_addr; +} + +bool +Address::SetOpcodeLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (SetLoadAddress (load_addr, target)) + { + if (target) + m_offset = target->GetOpcodeLoadAddress (m_offset, GetAddressClass()); + 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 NULL, 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.load()); + } + else + { + s->Address(m_offset, addr_size); + } + break; + + case DumpStyleSectionPointerOffset: + s->Printf("(Section *)%p + ", section_sp.get()); + s->Address(m_offset, addr_size); + break; + + case DumpStyleModuleWithFileAddress: + if (section_sp) + s->Printf("%s[", section_sp->GetModule()->GetFileSpec().GetFilename().AsCString()); + // Fall through + 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); + 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: + 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) + { + SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + 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->GetAddress().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 || func_sc.symbol) + { + 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); + 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 || pointer_sc.symbol) + { + s->PutCString(": "); + pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false); + } + } + } + } + } + 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; + if (sc.function == NULL && sc.symbol != NULL) + { + // If we have just a symbol make sure it is in the right section + if (sc.symbol->ValueIsAddress()) + { + if (sc.symbol->GetAddress().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); + } + 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, 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->GetAddress().GetSection() != GetSection()) + sc.symbol = NULL; + } + 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_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\", type= \"%s\", location =", + var->GetID(), + var->GetName().GetCString(), + var->GetType()->GetName().GetCString()); + 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) + { + Error 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.GetData(), strm.GetSize()); + return true; + } + } + } + } + } + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + break; + } + + return true; +} + +uint32_t +Address::CalculateSymbolContext (SymbolContext *sc, uint32_t 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 NULL; +} + +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 NULL; +} + +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 NULL; +} + +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 NULL; +} + +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 != NULL); + 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 vendor a chance to add to the unified section list. + module_sp->GetSymbolVendor(); + return obj_file->GetAddressClass (GetFileAddress()); + } + } + return eAddressClassUnknown; +} + +bool +Address::SetLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (target && target->GetSectionLoadList().ResolveLoadAddress(load_addr, *this)) + return true; + m_section_wp.reset(); + m_offset = load_addr; + return false; +} + diff --git a/source/Core/AddressRange.cpp b/source/Core/AddressRange.cpp new file mode 100644 index 0000000..835a01d --- /dev/null +++ b/source/Core/AddressRange.cpp @@ -0,0 +1,208 @@ +//===-- AddressRange.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +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; +} + +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; + // fall through + 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()); + } + 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", this, 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/source/Core/AddressResolver.cpp b/source/Core/AddressResolver.cpp new file mode 100644 index 0000000..5369d96 --- /dev/null +++ b/source/Core/AddressResolver.cpp @@ -0,0 +1,67 @@ +//===-- AddressResolver.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolver.h" + + +// Project includes + +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +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/source/Core/AddressResolverFileLine.cpp b/source/Core/AddressResolverFileLine.cpp new file mode 100644 index 0000000..f7004c8 --- /dev/null +++ b/source/Core/AddressResolverFileLine.cpp @@ -0,0 +1,102 @@ +//===-- AddressResolverFileLine.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolverFileLine.h" + +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/lldb-private-log.h" + +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, + bool containing +) +{ + 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); + //log->Printf ("Added address: %s\n", s.GetData()); + } + } + else + { + if (log) + log->Printf ("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; +} + +Searcher::Depth +AddressResolverFileLine::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +AddressResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("File and line address - file: \"%s\" line: %u", m_file_spec.GetFilename().AsCString(), m_line_number); +} + + diff --git a/source/Core/AddressResolverName.cpp b/source/Core/AddressResolverName.cpp new file mode 100644 index 0000000..dd22e17 --- /dev/null +++ b/source/Core/AddressResolverName.cpp @@ -0,0 +1,255 @@ +//===-- AddressResolverName.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolverName.h" + +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/lldb-private-log.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 (NULL), + m_regex (), + m_match_type (type) +{ + if (m_match_type == AddressResolver::Regexp) + { + if (!m_regex.Compile (m_func_name.AsCString())) + { + 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 (NULL), + m_class_name (NULL), + m_regex (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 () +{ +} + +// 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, + bool containing +) +{ + 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; + const bool append = false; + 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, + NULL, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + append, + 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, + append, + func_list); + } + break; + + case AddressResolver::Glob: + if (log) + log->Warning ("glob is not supported yet."); + break; + } + + // Remove any duplicates between the funcion list and the symbol list + if (func_list.GetSize()) + { + for (i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc) == false) + continue; + + if (sc.function == NULL) + 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->GetAddress()) + { + 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->GetAddress(); + 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; +} + +Searcher::Depth +AddressResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +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()); + else + s->Printf("'%s'", m_func_name.AsCString()); +} + diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp new file mode 100644 index 0000000..27d62c3 --- /dev/null +++ b/source/Core/ArchSpec.cpp @@ -0,0 +1,893 @@ +//===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ArchSpec.h" + +#include <stdio.h> +#include <errno.h> + +#include <string> + +#include "llvm/Support/ELF.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + +#define ARCH_SPEC_SEPARATOR_CHAR '-' + + +static bool cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match); + +namespace lldb_private { + + struct CoreDefinition + { + ByteOrder default_byte_order; + uint32_t addr_byte_size; + uint32_t min_opcode_byte_size; + uint32_t max_opcode_byte_size; + llvm::Triple::ArchType machine; + ArchSpec::Core core; + const char *name; + }; + +} + +// This core information can be looked using the ArchSpec::Core as the index +static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = +{ + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_generic , "arm" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv4 , "armv4" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv4t , "armv4t" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5 , "armv5" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5e , "armv5e" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5t , "armv5t" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv6 , "armv6" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7 , "armv7" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7f , "armv7f" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7s , "armv7s" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7k , "armv7k" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7m , "armv7m" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7em , "armv7em" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_xscale , "xscale" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumb , "thumb" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv4t , "thumbv4t" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5 , "thumbv5" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5e , "thumbv5e" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv6 , "thumbv6" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7 , "thumbv7" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7f , "thumbv7f" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7s , "thumbv7s" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7k , "thumbv7k" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7m , "thumbv7m" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7em , "thumbv7em" }, + + + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "ppc" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc601 , "ppc601" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc602 , "ppc602" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603 , "ppc603" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603e , "ppc603e" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603ev , "ppc603ev" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc604 , "ppc604" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc604e , "ppc604e" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc620 , "ppc620" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc750 , "ppc750" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7400 , "ppc7400" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7450 , "ppc7450" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc970 , "ppc970" }, + + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_generic , "ppc64" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_ppc970_64 , "ppc970-64" }, + + { eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc , ArchSpec::eCore_sparc_generic , "sparc" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9, ArchSpec::eCore_sparc9_generic , "sparcv9" }, + + { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i386 , "i386" }, + { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486 , "i486" }, + { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486sx , "i486sx" }, + + { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64 , "x86_64" }, + { eByteOrderLittle, 4, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach32 , "unknown-mach-32" }, + { eByteOrderLittle, 8, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach64 , "unknown-mach-64" } +}; + +struct ArchDefinitionEntry +{ + ArchSpec::Core core; + uint32_t cpu; + uint32_t sub; + uint32_t cpu_mask; + uint32_t sub_mask; +}; + +struct ArchDefinition +{ + ArchitectureType type; + size_t num_entries; + const ArchDefinitionEntry *entries; + const char *name; +}; + + +size_t +ArchSpec::AutoComplete (const char *name, StringList &matches) +{ + uint32_t i; + if (name && name[0]) + { + for (i = 0; i < ArchSpec::kNumCores; ++i) + { + if (NameMatches(g_core_definitions[i].name, eNameMatchStartsWith, name)) + matches.AppendString (g_core_definitions[i].name); + } + } + else + { + for (i = 0; i < ArchSpec::kNumCores; ++i) + matches.AppendString (g_core_definitions[i].name); + } + return matches.GetSize(); +} + + + +#define CPU_ANY (UINT32_MAX) + +//===----------------------------------------------------------------------===// +// A table that gets searched linearly for matches. This table is used to +// convert cpu type and subtypes to architecture names, and to convert +// architecture names to cpu types and subtypes. The ordering is important and +// allows the precedence to be set when the table is built. +#define SUBTYPE_MASK 0x00FFFFFFu +static const ArchDefinitionEntry g_macho_arch_entries[] = +{ + { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_xscale , llvm::MachO::CPUTypeARM , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumb , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPUTypePowerPC , 1 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPUTypePowerPC , 2 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPUTypePowerPC , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPUTypePowerPC , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPUTypePowerPC , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPUTypePowerPC , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPUTypePowerPC , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPUTypePowerPC , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPUTypePowerPC , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPUTypePowerPC , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPUTypePowerPC , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPUTypePowerPC , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPUTypePowerPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPUTypePowerPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPUTypeI386 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPUTypeI386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + // Catch any unknown mach architectures so we can always use the object and symbol mach-o files + { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, + { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPUArchABI64 , 0 , 0xFF000000u, 0x00000000u } +}; +static const ArchDefinition g_macho_arch_def = { + eArchTypeMachO, + sizeof(g_macho_arch_entries)/sizeof(g_macho_arch_entries[0]), + g_macho_arch_entries, + "mach-o" +}; + +//===----------------------------------------------------------------------===// +// A table that gets searched linearly for matches. This table is used to +// convert cpu type and subtypes to architecture names, and to convert +// architecture names to cpu types and subtypes. The ordering is important and +// allows the precedence to be set when the table is built. +static const ArchDefinitionEntry g_elf_arch_entries[] = +{ + { ArchSpec::eCore_sparc_generic , llvm::ELF::EM_SPARC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Sparc + { ArchSpec::eCore_x86_32_i386 , llvm::ELF::EM_386 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 80386 + { ArchSpec::eCore_x86_32_i486 , llvm::ELF::EM_486 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 486 (deprecated) + { ArchSpec::eCore_ppc_generic , llvm::ELF::EM_PPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC + { ArchSpec::eCore_ppc64_generic , llvm::ELF::EM_PPC64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC64 + { ArchSpec::eCore_arm_generic , llvm::ELF::EM_ARM , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM + { ArchSpec::eCore_sparc9_generic , llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // SPARC V9 + { ArchSpec::eCore_x86_64_x86_64 , llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // AMD64 +}; + +static const ArchDefinition g_elf_arch_def = { + eArchTypeELF, + sizeof(g_elf_arch_entries)/sizeof(g_elf_arch_entries[0]), + g_elf_arch_entries, + "elf", +}; + +//===----------------------------------------------------------------------===// +// Table of all ArchDefinitions +static const ArchDefinition *g_arch_definitions[] = { + &g_macho_arch_def, + &g_elf_arch_def +}; + +static const size_t k_num_arch_definitions = + sizeof(g_arch_definitions) / sizeof(g_arch_definitions[0]); + +//===----------------------------------------------------------------------===// +// Static helper functions. + + +// Get the architecture definition for a given object type. +static const ArchDefinition * +FindArchDefinition (ArchitectureType arch_type) +{ + for (unsigned int i = 0; i < k_num_arch_definitions; ++i) + { + const ArchDefinition *def = g_arch_definitions[i]; + if (def->type == arch_type) + return def; + } + return NULL; +} + +// Get an architecture definition by name. +static const CoreDefinition * +FindCoreDefinition (llvm::StringRef name) +{ + for (unsigned int i = 0; i < ArchSpec::kNumCores; ++i) + { + if (name.equals_lower(g_core_definitions[i].name)) + return &g_core_definitions[i]; + } + return NULL; +} + +static inline const CoreDefinition * +FindCoreDefinition (ArchSpec::Core core) +{ + if (core >= 0 && core < ArchSpec::kNumCores) + return &g_core_definitions[core]; + return NULL; +} + +// Get a definition entry by cpu type and subtype. +static const ArchDefinitionEntry * +FindArchDefinitionEntry (const ArchDefinition *def, uint32_t cpu, uint32_t sub) +{ + if (def == NULL) + return NULL; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) + { + if (entries[i].cpu == (cpu & entries[i].cpu_mask)) + if (entries[i].sub == (sub & entries[i].sub_mask)) + return &entries[i]; + } + return NULL; +} + +static const ArchDefinitionEntry * +FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) +{ + if (def == NULL) + return NULL; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) + { + if (entries[i].core == core) + return &entries[i]; + } + return NULL; +} + +//===----------------------------------------------------------------------===// +// Constructors and destructors. + +ArchSpec::ArchSpec() : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ +} + +ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + if (triple_cstr) + SetTriple(triple_cstr, platform); +} + + +ArchSpec::ArchSpec (const char *triple_cstr) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + if (triple_cstr) + SetTriple(triple_cstr); +} + +ArchSpec::ArchSpec(const llvm::Triple &triple) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + SetTriple(triple); +} + +ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + SetArchitecture (arch_type, cpu, subtype); +} + +ArchSpec::~ArchSpec() +{ +} + +//===----------------------------------------------------------------------===// +// Assignment and initialization. + +const ArchSpec& +ArchSpec::operator= (const ArchSpec& rhs) +{ + if (this != &rhs) + { + m_triple = rhs.m_triple; + m_core = rhs.m_core; + m_byte_order = rhs.m_byte_order; + } + return *this; +} + +void +ArchSpec::Clear() +{ + m_triple = llvm::Triple(); + m_core = kCore_invalid; + m_byte_order = eByteOrderInvalid; +} + +//===----------------------------------------------------------------------===// +// Predicates. + + +const char * +ArchSpec::GetArchitectureName () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->name; + return "unknown"; +} + +uint32_t +ArchSpec::GetMachOCPUType () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + { + const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry (&g_macho_arch_def, core_def->core); + if (arch_def) + { + return arch_def->cpu; + } + } + return LLDB_INVALID_CPUTYPE; +} + +uint32_t +ArchSpec::GetMachOCPUSubType () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + { + const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry (&g_macho_arch_def, core_def->core); + if (arch_def) + { + return arch_def->sub; + } + } + return LLDB_INVALID_CPUTYPE; +} + +llvm::Triple::ArchType +ArchSpec::GetMachine () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->machine; + + return llvm::Triple::UnknownArch; +} + +uint32_t +ArchSpec::GetAddressByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->addr_byte_size; + return 0; +} + +ByteOrder +ArchSpec::GetDefaultEndian () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->default_byte_order; + return eByteOrderInvalid; +} + +lldb::ByteOrder +ArchSpec::GetByteOrder () const +{ + if (m_byte_order == eByteOrderInvalid) + return GetDefaultEndian(); + return m_byte_order; +} + +//===----------------------------------------------------------------------===// +// Mutators. + +bool +ArchSpec::SetTriple (const llvm::Triple &triple) +{ + m_triple = triple; + + llvm::StringRef arch_name (m_triple.getArchName()); + const CoreDefinition *core_def = FindCoreDefinition (arch_name); + if (core_def) + { + m_core = core_def->core; + // Set the byte order to the default byte order for an architecture. + // This can be modified if needed for cases when cores handle both + // big and little endian + m_byte_order = core_def->default_byte_order; + } + else + { + Clear(); + } + + + return IsValid(); +} + +static bool +ParseMachCPUDashSubtypeTriple (const char *triple_cstr, ArchSpec &arch) +{ + // Accept "12-10" or "12.10" as cpu type/subtype + if (isdigit(triple_cstr[0])) + { + char *end = NULL; + errno = 0; + uint32_t cpu = (uint32_t)::strtoul (triple_cstr, &end, 0); + if (errno == 0 && cpu != 0 && end && ((*end == '-') || (*end == '.'))) + { + errno = 0; + uint32_t sub = (uint32_t)::strtoul (end + 1, &end, 0); + if (errno == 0 && end && ((*end == '-') || (*end == '.') || (*end == '\0'))) + { + if (arch.SetArchitecture (eArchTypeMachO, cpu, sub)) + { + if (*end == '-') + { + llvm::StringRef vendor_os (end + 1); + size_t dash_pos = vendor_os.find('-'); + if (dash_pos != llvm::StringRef::npos) + { + llvm::StringRef vendor_str(vendor_os.substr(0, dash_pos)); + arch.GetTriple().setVendorName(vendor_str); + const size_t vendor_start_pos = dash_pos+1; + dash_pos = vendor_os.find('-', vendor_start_pos); + if (dash_pos == llvm::StringRef::npos) + { + if (vendor_start_pos < vendor_os.size()) + arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos)); + } + else + { + arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos, dash_pos - vendor_start_pos)); + } + } + } + return true; + } + } + } + } + return false; +} +bool +ArchSpec::SetTriple (const char *triple_cstr) +{ + if (triple_cstr && triple_cstr[0]) + { + if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) + return true; + + llvm::StringRef triple_stref (triple_cstr); + if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) + { + // Special case for the current host default architectures... + if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + } + else + { + std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); + triple_stref = normalized_triple_sstr; + SetTriple (llvm::Triple (triple_stref)); + } + } + else + Clear(); + return IsValid(); +} + +bool +ArchSpec::SetTriple (const char *triple_cstr, Platform *platform) +{ + if (triple_cstr && triple_cstr[0]) + { + if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) + return true; + + llvm::StringRef triple_stref (triple_cstr); + if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) + { + // Special case for the current host default architectures... + if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + } + else + { + ArchSpec raw_arch (triple_cstr); + + std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); + triple_stref = normalized_triple_sstr; + llvm::Triple normalized_triple (triple_stref); + + const bool os_specified = normalized_triple.getOSName().size() > 0; + const bool vendor_specified = normalized_triple.getVendorName().size() > 0; + const bool env_specified = normalized_triple.getEnvironmentName().size() > 0; + + // If we got an arch only, then default the vendor, os, environment + // to match the platform if one is supplied + if (!(os_specified || vendor_specified || env_specified)) + { + if (platform) + { + // If we were given a platform, use the platform's system + // architecture. If this is not available (might not be + // connected) use the first supported architecture. + ArchSpec compatible_arch; + if (platform->IsCompatibleArchitecture (raw_arch, false, &compatible_arch)) + { + if (compatible_arch.IsValid()) + { + const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); + if (!vendor_specified) + normalized_triple.setVendor(compatible_triple.getVendor()); + if (!os_specified) + normalized_triple.setOS(compatible_triple.getOS()); + if (!env_specified && compatible_triple.getEnvironmentName().size()) + normalized_triple.setEnvironment(compatible_triple.getEnvironment()); + } + } + else + { + *this = raw_arch; + return IsValid(); + } + } + else + { + // No platform specified, fall back to the host system for + // the default vendor, os, and environment. + llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple()); + if (!vendor_specified) + normalized_triple.setVendor(host_triple.getVendor()); + if (!vendor_specified) + normalized_triple.setOS(host_triple.getOS()); + if (!env_specified && host_triple.getEnvironmentName().size()) + normalized_triple.setEnvironment(host_triple.getEnvironment()); + } + } + SetTriple (normalized_triple); + } + } + else + Clear(); + return IsValid(); +} + +bool +ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub) +{ + m_core = kCore_invalid; + bool update_triple = true; + const ArchDefinition *arch_def = FindArchDefinition(arch_type); + if (arch_def) + { + const ArchDefinitionEntry *arch_def_entry = FindArchDefinitionEntry (arch_def, cpu, sub); + if (arch_def_entry) + { + const CoreDefinition *core_def = FindCoreDefinition (arch_def_entry->core); + if (core_def) + { + m_core = core_def->core; + update_triple = false; + // Always use the architecture name because it might be more descriptive + // than the architecture enum ("armv7" -> llvm::Triple::arm). + m_triple.setArchName(llvm::StringRef(core_def->name)); + if (arch_type == eArchTypeMachO) + { + m_triple.setVendor (llvm::Triple::Apple); + + switch (core_def->machine) + { + case llvm::Triple::arm: + case llvm::Triple::thumb: + m_triple.setOS (llvm::Triple::IOS); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + default: + m_triple.setOS (llvm::Triple::MacOSX); + break; + } + } + else + { + m_triple.setVendor (llvm::Triple::UnknownVendor); + m_triple.setOS (llvm::Triple::UnknownOS); + } + // Fall back onto setting the machine type if the arch by name failed... + if (m_triple.getArch () == llvm::Triple::UnknownArch) + m_triple.setArch (core_def->machine); + } + } + } + CoreUpdated(update_triple); + return IsValid(); +} + +uint32_t +ArchSpec::GetMinimumOpcodeByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->min_opcode_byte_size; + return 0; +} + +uint32_t +ArchSpec::GetMaximumOpcodeByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->max_opcode_byte_size; + return 0; +} + +bool +ArchSpec::IsExactMatch (const ArchSpec& rhs) const +{ + return IsEqualTo (rhs, true); +} + +bool +ArchSpec::IsCompatibleMatch (const ArchSpec& rhs) const +{ + return IsEqualTo (rhs, false); +} + +bool +ArchSpec::IsEqualTo (const ArchSpec& rhs, bool exact_match) const +{ + if (GetByteOrder() != rhs.GetByteOrder()) + return false; + + const ArchSpec::Core lhs_core = GetCore (); + const ArchSpec::Core rhs_core = rhs.GetCore (); + + const bool core_match = cores_match (lhs_core, rhs_core, true, exact_match); + + if (core_match) + { + const llvm::Triple &lhs_triple = GetTriple(); + const llvm::Triple &rhs_triple = rhs.GetTriple(); + + const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor(); + const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor(); + if (lhs_triple_vendor != rhs_triple_vendor) + { + if (exact_match) + { + const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified(); + const bool lhs_vendor_specified = TripleVendorWasSpecified(); + // Both architectures had the vendor specified, so if they aren't + // equal then we return false + if (rhs_vendor_specified && lhs_vendor_specified) + return false; + } + + // Only fail if both vendor types are not unknown + if (lhs_triple_vendor != llvm::Triple::UnknownVendor && + rhs_triple_vendor != llvm::Triple::UnknownVendor) + return false; + } + + const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS(); + const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); + if (lhs_triple_os != rhs_triple_os) + { + if (exact_match) + { + const bool rhs_os_specified = rhs.TripleOSWasSpecified(); + const bool lhs_os_specified = TripleOSWasSpecified(); + // Both architectures had the OS specified, so if they aren't + // equal then we return false + if (rhs_os_specified && lhs_os_specified) + return false; + } + // Only fail if both os types are not unknown + if (lhs_triple_os != llvm::Triple::UnknownOS && + rhs_triple_os != llvm::Triple::UnknownOS) + return false; + } + + const llvm::Triple::EnvironmentType lhs_triple_env = lhs_triple.getEnvironment(); + const llvm::Triple::EnvironmentType rhs_triple_env = rhs_triple.getEnvironment(); + + if (lhs_triple_env != rhs_triple_env) + { + // Only fail if both environment types are not unknown + if (lhs_triple_env != llvm::Triple::UnknownEnvironment && + rhs_triple_env != llvm::Triple::UnknownEnvironment) + return false; + } + return true; + } + return false; +} + +//===----------------------------------------------------------------------===// +// Helper methods. + +void +ArchSpec::CoreUpdated (bool update_triple) +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + { + if (update_triple) + m_triple = llvm::Triple(core_def->name, "unknown", "unknown"); + m_byte_order = core_def->default_byte_order; + } + else + { + if (update_triple) + m_triple = llvm::Triple(); + m_byte_order = eByteOrderInvalid; + } +} + +//===----------------------------------------------------------------------===// +// Operators. + +static bool +cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match) +{ + if (core1 == core2) + return true; + + switch (core1) + { + case ArchSpec::kCore_any: + return true; + + case ArchSpec::kCore_arm_any: + if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last) + return true; + if (core2 >= ArchSpec::kCore_thumb_first && core2 <= ArchSpec::kCore_thumb_last) + return true; + if (core2 == ArchSpec::kCore_arm_any) + return true; + break; + + case ArchSpec::kCore_x86_32_any: + if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) + return true; + break; + + case ArchSpec::kCore_ppc_any: + if ((core2 >= ArchSpec::kCore_ppc_first && core2 <= ArchSpec::kCore_ppc_last) || (core2 == ArchSpec::kCore_ppc_any)) + return true; + break; + + case ArchSpec::kCore_ppc64_any: + if ((core2 >= ArchSpec::kCore_ppc64_first && core2 <= ArchSpec::kCore_ppc64_last) || (core2 == ArchSpec::kCore_ppc64_any)) + return true; + break; + + case ArchSpec::eCore_arm_armv7m: + case ArchSpec::eCore_arm_armv7em: + case ArchSpec::eCore_arm_armv7f: + case ArchSpec::eCore_arm_armv7k: + case ArchSpec::eCore_arm_armv7s: + if (!enforce_exact_match) + { + try_inverse = false; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + } + break; + + default: + break; + } + if (try_inverse) + return cores_match (core2, core1, false, enforce_exact_match); + return false; +} + +bool +lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs) +{ + const ArchSpec::Core lhs_core = lhs.GetCore (); + const ArchSpec::Core rhs_core = rhs.GetCore (); + return lhs_core < rhs_core; +} diff --git a/source/Core/Baton.cpp b/source/Core/Baton.cpp new file mode 100644 index 0000000..8bed01b --- /dev/null +++ b/source/Core/Baton.cpp @@ -0,0 +1,24 @@ +//===-- Baton.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Baton.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +Baton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ +} diff --git a/source/Core/Broadcaster.cpp b/source/Core/Broadcaster.cpp new file mode 100644 index 0000000..5af7497 --- /dev/null +++ b/source/Core/Broadcaster.cpp @@ -0,0 +1,499 @@ +//===-- Broadcaster.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Broadcaster.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamString.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +Broadcaster::Broadcaster (BroadcasterManager *manager, const char *name) : + m_broadcaster_name (name), + m_listeners (), + m_listeners_mutex (Mutex::eMutexTypeRecursive), + m_hijacking_listeners(), + m_hijacking_masks(), + m_manager (manager) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Broadcaster::Broadcaster(\"%s\")", this, m_broadcaster_name.AsCString()); + +} + +Broadcaster::~Broadcaster() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Broadcaster::~Broadcaster(\"%s\")", this, m_broadcaster_name.AsCString()); + + Clear(); +} + +void +Broadcaster::CheckInWithManager () +{ + if (m_manager != NULL) + { + m_manager->SignUpListenersForBroadcaster(*this); + } +} + +void +Broadcaster::Clear() +{ + Mutex::Locker listeners_locker(m_listeners_mutex); + + // Make sure the listener forgets about this broadcaster. We do + // this in the broadcaster in case the broadcaster object initiates + // the removal. + + collection::iterator pos, end = m_listeners.end(); + for (pos = m_listeners.begin(); pos != end; ++pos) + pos->first->BroadcasterWillDestruct (this); + + m_listeners.clear(); +} +const ConstString & +Broadcaster::GetBroadcasterName () +{ + return m_broadcaster_name; +} + +bool +Broadcaster::GetEventNames (Stream &s, uint32_t event_mask, bool prefix_with_broadcaster_name) const +{ + uint32_t num_names_added = 0; + if (event_mask && !m_event_names.empty()) + { + event_names_map::const_iterator end = m_event_names.end(); + for (uint32_t bit=1u, mask=event_mask; mask != 0 && bit != 0; bit <<= 1, mask >>= 1) + { + if (mask & 1) + { + event_names_map::const_iterator pos = m_event_names.find(bit); + if (pos != end) + { + if (num_names_added > 0) + s.PutCString(", "); + + if (prefix_with_broadcaster_name) + { + s.PutCString (m_broadcaster_name.GetCString()); + s.PutChar('.'); + } + s.PutCString(pos->second.c_str()); + ++num_names_added; + } + } + } + } + return num_names_added > 0; +} + +void +Broadcaster::AddInitialEventsToListener (Listener *listener, uint32_t requested_events) +{ + +} + +uint32_t +Broadcaster::AddListener (Listener* listener, uint32_t event_mask) +{ + if (listener == NULL) + return 0; + + Mutex::Locker locker(m_listeners_mutex); + collection::iterator pos, end = m_listeners.end(); + + collection::iterator existing_pos = end; + // See if we already have this listener, and if so, update its mask + uint32_t taken_event_types = 0; + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->first == listener) + existing_pos = pos; + // For now don't descriminate on who gets what + // FIXME: Implement "unique listener for this bit" mask + // taken_event_types |= pos->second; + } + + // Each event bit in a Broadcaster object can only be used + // by one listener + uint32_t available_event_types = ~taken_event_types & event_mask; + + if (available_event_types) + { + // If we didn't find our listener, add it + if (existing_pos == end) + { + // Grant a new listener the available event bits + m_listeners.push_back(std::make_pair(listener, available_event_types)); + } + else + { + // Grant the existing listener the available event bits + existing_pos->second |= available_event_types; + } + + // Individual broadcasters decide whether they have outstanding data when a + // listener attaches, and insert it into the listener with this method. + + AddInitialEventsToListener (listener, available_event_types); + } + + // Return the event bits that were granted to the listener + return available_event_types; +} + +bool +Broadcaster::EventTypeHasListeners (uint32_t event_type) +{ + Mutex::Locker locker (m_listeners_mutex); + + if (m_hijacking_listeners.size() > 0 && event_type & m_hijacking_masks.back()) + return true; + + if (m_listeners.empty()) + return false; + + collection::iterator pos, end = m_listeners.end(); + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->second & event_type) + return true; + } + return false; +} + +bool +Broadcaster::RemoveListener (Listener* listener, uint32_t event_mask) +{ + Mutex::Locker locker(m_listeners_mutex); + collection::iterator pos, end = m_listeners.end(); + // See if we already have this listener, and if so, update its mask + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->first == listener) + { + // Relinquish all event bits in "event_mask" + pos->second &= ~event_mask; + // If all bits have been relinquished then remove this listener + if (pos->second == 0) + m_listeners.erase (pos); + return true; + } + } + return false; +} + +void +Broadcaster::BroadcastEvent (EventSP &event_sp) +{ + return PrivateBroadcastEvent (event_sp, false); +} + +void +Broadcaster::BroadcastEventIfUnique (EventSP &event_sp) +{ + return PrivateBroadcastEvent (event_sp, true); +} + +void +Broadcaster::PrivateBroadcastEvent (EventSP &event_sp, bool unique) +{ + // Can't add a NULL event... + if (event_sp.get() == NULL) + return; + + // Update the broadcaster on this event + event_sp->SetBroadcaster (this); + + const uint32_t event_type = event_sp->GetType(); + + Mutex::Locker event_types_locker(m_listeners_mutex); + + Listener *hijacking_listener = NULL; + if (!m_hijacking_listeners.empty()) + { + assert (!m_hijacking_masks.empty()); + hijacking_listener = m_hijacking_listeners.back(); + if ((event_type & m_hijacking_masks.back()) == 0) + hijacking_listener = NULL; + } + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + StreamString event_description; + event_sp->Dump (&event_description); + log->Printf ("%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, unique =%i) hijack = %p", + this, + m_broadcaster_name.AsCString(""), + event_description.GetData(), + unique, + hijacking_listener); + } + + if (hijacking_listener) + { + if (unique && hijacking_listener->PeekAtNextEventForBroadcasterWithType (this, event_type)) + return; + hijacking_listener->AddEvent (event_sp); + } + else + { + collection::iterator pos, end = m_listeners.end(); + + + // Iterate through all listener/mask pairs + for (pos = m_listeners.begin(); pos != end; ++pos) + { + // If the listener's mask matches any bits that we just set, then + // put the new event on its event queue. + if (event_type & pos->second) + { + if (unique && pos->first->PeekAtNextEventForBroadcasterWithType (this, event_type)) + continue; + pos->first->AddEvent (event_sp); + } + } + } +} + +void +Broadcaster::BroadcastEvent (uint32_t event_type, EventData *event_data) +{ + EventSP event_sp (new Event (event_type, event_data)); + PrivateBroadcastEvent (event_sp, false); +} + +void +Broadcaster::BroadcastEventIfUnique (uint32_t event_type, EventData *event_data) +{ + EventSP event_sp (new Event (event_type, event_data)); + PrivateBroadcastEvent (event_sp, true); +} + +bool +Broadcaster::HijackBroadcaster (Listener *listener, uint32_t event_mask) +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + log->Printf ("%p Broadcaster(\"%s\")::HijackBroadcaster (listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.push_back(listener); + m_hijacking_masks.push_back(event_mask); + return true; +} + +void +Broadcaster::RestoreBroadcaster () +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + Listener *listener = m_hijacking_listeners.back(); + log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.pop_back(); + m_hijacking_masks.pop_back(); +} + +ConstString & +Broadcaster::GetBroadcasterClass() const +{ + static ConstString class_name ("lldb.anonymous"); + return class_name; +} + +BroadcastEventSpec::BroadcastEventSpec (const BroadcastEventSpec &rhs) : + m_broadcaster_class (rhs.m_broadcaster_class), + m_event_bits (rhs.m_event_bits) +{ +} + +bool +BroadcastEventSpec::operator< (const BroadcastEventSpec &rhs) const +{ + if (GetBroadcasterClass() == rhs.GetBroadcasterClass()) + { + return GetEventBits() < rhs.GetEventBits(); + } + else + { + return GetBroadcasterClass() < rhs.GetBroadcasterClass(); + } +} + +const BroadcastEventSpec & +BroadcastEventSpec::operator= (const BroadcastEventSpec &rhs) +{ + m_broadcaster_class = rhs.m_broadcaster_class; + m_event_bits = rhs.m_event_bits; + return *this; +} + +BroadcasterManager::BroadcasterManager() : + m_manager_mutex(Mutex::eMutexTypeRecursive) +{ + +} + +uint32_t +BroadcasterManager::RegisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec) +{ + Mutex::Locker locker(m_manager_mutex); + + collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + uint32_t available_bits = event_spec.GetEventBits(); + + while (iter != end_iter + && (iter = find_if (iter, end_iter, BroadcasterClassMatches(event_spec.GetBroadcasterClass()))) != end_iter) + { + available_bits &= ~((*iter).first.GetEventBits()); + iter++; + } + + if (available_bits != 0) + { + m_event_map.insert (event_listener_key (BroadcastEventSpec (event_spec.GetBroadcasterClass(), available_bits), &listener)); + m_listeners.insert(&listener); + } + + return available_bits; +} + +bool +BroadcasterManager::UnregisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec) +{ + Mutex::Locker locker(m_manager_mutex); + bool removed_some = false; + + if (m_listeners.erase(&listener) == 0) + return false; + + ListenerMatchesAndSharedBits predicate (event_spec, listener); + std::vector<BroadcastEventSpec> to_be_readded; + uint32_t event_bits_to_remove = event_spec.GetEventBits(); + + // Go through the map and delete the exact matches, and build a list of matches that weren't exact to re-add: + while (1) + { + collection::iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, predicate); + if (iter == end_iter) + { + break; + } + else + { + uint32_t iter_event_bits = (*iter).first.GetEventBits(); + removed_some = true; + + if (event_bits_to_remove != iter_event_bits) + { + uint32_t new_event_bits = iter_event_bits & ~event_bits_to_remove; + to_be_readded.push_back(BroadcastEventSpec (event_spec.GetBroadcasterClass(), new_event_bits)); + } + m_event_map.erase (iter); + } + } + + // Okay now add back the bits that weren't completely removed: + for (size_t i = 0; i < to_be_readded.size(); i++) + { + m_event_map.insert (event_listener_key (to_be_readded[i], &listener)); + } + + return removed_some; +} + +Listener * +BroadcasterManager::GetListenerForEventSpec (BroadcastEventSpec event_spec) const +{ + Mutex::Locker locker(*(const_cast<Mutex *> (&m_manager_mutex))); + + collection::const_iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, BroadcastEventSpecMatches (event_spec)); + if (iter != end_iter) + return (*iter).second; + else + return NULL; +} + +void +BroadcasterManager::RemoveListener (Listener &listener) +{ + Mutex::Locker locker(m_manager_mutex); + ListenerMatches predicate (listener); + + + if (m_listeners.erase (&listener) == 0) + return; + + while (1) + { + collection::iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, predicate); + if (iter == end_iter) + break; + else + m_event_map.erase(iter); + } +} + +void +BroadcasterManager::SignUpListenersForBroadcaster (Broadcaster &broadcaster) +{ + Mutex::Locker locker(m_manager_mutex); + + collection::iterator iter = m_event_map.begin(), end_iter = m_event_map.end(); + + while (iter != end_iter + && (iter = find_if (iter, end_iter, BroadcasterClassMatches(broadcaster.GetBroadcasterClass()))) != end_iter) + { + (*iter).second->StartListeningForEvents (&broadcaster, (*iter).first.GetEventBits()); + iter++; + } +} + +void +BroadcasterManager::Clear () +{ + Mutex::Locker locker(m_manager_mutex); + listener_collection::iterator end_iter = m_listeners.end(); + + for (listener_collection::iterator iter = m_listeners.begin(); iter != end_iter; iter++) + (*iter)->BroadcasterManagerWillDestruct(this); + m_listeners.clear(); + m_event_map.clear(); + +} diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp new file mode 100644 index 0000000..7f40e65 --- /dev/null +++ b/source/Core/Communication.cpp @@ -0,0 +1,431 @@ +//===-- Communication.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Connection.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Event.h" +#include "lldb/Host/Host.h" +#include <string.h> + +using namespace lldb; +using namespace lldb_private; + +ConstString & +Communication::GetStaticBroadcasterClass () +{ + static ConstString class_name ("lldb.communication"); + return class_name; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +Communication::Communication(const char *name) : + Broadcaster (NULL, name), + m_connection_sp (), + m_read_thread (LLDB_INVALID_HOST_THREAD), + m_read_thread_enabled (false), + m_bytes(), + m_bytes_mutex (Mutex::eMutexTypeRecursive), + m_write_mutex (Mutex::eMutexTypeNormal), + m_callback (NULL), + m_callback_baton (NULL), + m_close_on_eof (true) + +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Communication (name = %s)", + 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"); + + CheckInWithManager(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Communication::~Communication() +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::~Communication (name = %s)", + this, m_broadcaster_name.AsCString("")); + Clear(); +} + +void +Communication::Clear() +{ + SetReadThreadBytesReceivedCallback (NULL, NULL); + Disconnect (NULL); + StopReadThread (NULL); +} + +ConnectionStatus +Communication::Connect (const char *url, Error *error_ptr) +{ + Clear(); + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Connect (url = %s)", this, url); + + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->Connect (url, error_ptr); + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +Communication::Disconnect (Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Disconnect ()", this); + + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + { + 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 disconnnect. + //connection_sp.reset(); + return status; + } + return eConnectionStatusNoConnection; +} + +bool +Communication::IsConnected () const +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->IsConnected (); + return false; +} + +bool +Communication::HasConnection () const +{ + return m_connection_sp.get() != NULL; +} + +size_t +Communication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Read (dst = %p, dst_len = %" PRIu64 ", timeout = %u usec) connection = %p", + this, + dst, + (uint64_t)dst_len, + timeout_usec, + 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_usec == 0) + { + status = eConnectionStatusSuccess; + return cached_bytes; + } + + if (m_connection_sp.get() == NULL) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; + } + // Set the timeout appropriately + TimeValue timeout_time; + if (timeout_usec != UINT32_MAX) + { + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithMicroSeconds (timeout_usec); + } + + Listener listener ("Communication::Read"); + listener.StartListeningForEvents (this, eBroadcastBitReadThreadGotBytes | eBroadcastBitReadThreadDidExit); + EventSP event_sp; + while (listener.WaitForEvent (timeout_time.IsValid() ? &timeout_time : NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_type & eBroadcastBitReadThreadGotBytes) + { + return GetCachedBytes (dst, dst_len); + } + + if (event_type & eBroadcastBitReadThreadDidExit) + { + Disconnect (NULL); + break; + } + } + return 0; + } + + // We aren't using a read thread, just read the data synchronously in this + // thread. + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + { + return connection_sp->Read (dst, dst_len, timeout_usec, status, error_ptr); + } + + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; +} + + +size_t +Communication::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + + Mutex::Locker locker(m_write_mutex); + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Write (src = %p, src_len = %" PRIu64 ") connection = %p", + this, + src, + (uint64_t)src_len, + connection_sp.get()); + + if (connection_sp.get()) + 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 (Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + + if (IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::StartReadThread ()", this); + + + char thread_name[1024]; + snprintf(thread_name, sizeof(thread_name), "<lldb.comm.%s>", m_broadcaster_name.AsCString()); + + m_read_thread_enabled = true; + m_read_thread = Host::ThreadCreate (thread_name, Communication::ReadThread, this, error_ptr); + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + m_read_thread_enabled = false; + return m_read_thread_enabled; +} + +bool +Communication::StopReadThread (Error *error_ptr) +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::StopReadThread ()", this); + + m_read_thread_enabled = false; + + BroadcastEvent (eBroadcastBitReadThreadShouldExit, NULL); + + //Host::ThreadCancel (m_read_thread, error_ptr); + + bool status = Host::ThreadJoin (m_read_thread, NULL, error_ptr); + m_read_thread = LLDB_INVALID_HOST_THREAD; + return status; +} + + +size_t +Communication::GetCachedBytes (void *dst, size_t dst_len) +{ + Mutex::Locker locker(m_bytes_mutex); + if (m_bytes.size() > 0) + { + // If DST is NULL and we have a thread, then return the number + // of bytes that are available so the caller can call again + if (dst == NULL) + 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_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::AppendBytesToCache (src = %p, src_len = %" PRIu64 ", broadcast = %i)", + this, bytes, (uint64_t)len, broadcast); + if ((bytes == NULL || 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 != NULL && len > 0) + { + Mutex::Locker locker(m_bytes_mutex); + m_bytes.append ((const char *)bytes, len); + if (broadcast) + BroadcastEventIfUnique (eBroadcastBitReadThreadGotBytes); + } +} + +size_t +Communication::ReadFromConnection (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->Read (dst, dst_len, timeout_usec, status, error_ptr); + return 0; +} + +bool +Communication::ReadThreadIsRunning () +{ + return m_read_thread_enabled; +} + +void * +Communication::ReadThread (void *p) +{ + Communication *comm = (Communication *)p; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); + + if (log) + log->Printf ("%p Communication::ReadThread () thread starting...", p); + + uint8_t buf[1024]; + + Error error; + ConnectionStatus status = eConnectionStatusSuccess; + bool done = false; + while (!done && comm->m_read_thread_enabled) + { + size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), 5 * TimeValue::MicroSecPerSec, 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: + if (comm->GetCloseOnEOF()) + done = true; + break; + case eConnectionStatusNoConnection: // No connection + case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + done = true; + // Fall through... + case eConnectionStatusError: // Check GetError() for details + case eConnectionStatusTimedOut: // Request timed out + if (log) + error.LogIfError (log, + "%p Communication::ReadFromConnection () => status = %s", + p, + Communication::ConnectionStatusAsCString (status)); + break; + } + } + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION); + if (log) + log->Printf ("%p Communication::ReadThread () thread exiting...", p); + + // Let clients know that this thread is exiting + comm->BroadcastEvent (eBroadcastBitReadThreadDidExit); + return NULL; +} + +void +Communication::SetReadThreadBytesReceivedCallback +( + ReadThreadBytesReceived callback, + void *callback_baton +) +{ + m_callback = callback; + m_callback_baton = callback_baton; +} + +void +Communication::SetConnection (Connection *connection) +{ + Disconnect (NULL); + StopReadThread(NULL); + 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"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "ConnectionStatus = %i", status); + return unknown_state_string; +} diff --git a/source/Core/Connection.cpp b/source/Core/Connection.cpp new file mode 100644 index 0000000..3c9bb8b --- /dev/null +++ b/source/Core/Connection.cpp @@ -0,0 +1,24 @@ +//===-- Connection.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" + +using namespace lldb_private; + +Connection::Connection () +{ +} + +Connection::~Connection () +{ +} diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Core/ConnectionFileDescriptor.cpp new file mode 100644 index 0000000..e320bda --- /dev/null +++ b/source/Core/ConnectionFileDescriptor.cpp @@ -0,0 +1,1528 @@ +//===-- ConnectionFileDescriptor.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +#include "lldb/Core/ConnectionFileDescriptor.h" + +// C Includes +#include <errno.h> +#include <fcntl.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <termios.h> +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +// C++ Includes +// Other libraries and framework includes +#if defined(__APPLE__) +#include "llvm/ADT/SmallVector.h" +#endif +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +DecodeHostAndPort (const char *host_and_port, + std::string &host_str, + std::string &port_str, + int32_t& port, + Error *error_ptr) +{ + static RegularExpression g_regex ("([^:]+):([0-9]+)"); + RegularExpression::Match regex_match(2); + if (g_regex.Execute (host_and_port, ®ex_match)) + { + if (regex_match.GetMatchAtIndex (host_and_port, 1, host_str) && + regex_match.GetMatchAtIndex (host_and_port, 2, port_str)) + { + port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN); + if (port != INT32_MIN) + { + if (error_ptr) + error_ptr->Clear(); + return true; + } + } + } + host_str.clear(); + port_str.clear(); + port = INT32_MIN; + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port); + return false; +} + +ConnectionFileDescriptor::ConnectionFileDescriptor () : + Connection(), + m_fd_send (-1), + m_fd_recv (-1), + m_fd_send_type (eFDTypeFile), + m_fd_recv_type (eFDTypeFile), + m_udp_send_sockaddr (), + m_should_close_fd (false), + m_socket_timeout_usec(0), + m_pipe_read(-1), + m_pipe_write(-1), + m_mutex (Mutex::eMutexTypeRecursive), + m_shutting_down (false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", this); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : + Connection(), + m_fd_send (fd), + m_fd_recv (fd), + m_fd_send_type (eFDTypeFile), + m_fd_recv_type (eFDTypeFile), + m_udp_send_sockaddr (), + m_should_close_fd (owns_fd), + m_socket_timeout_usec(0), + m_pipe_read(-1), + m_pipe_write(-1), + m_mutex (Mutex::eMutexTypeRecursive), + m_shutting_down (false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", this, fd, owns_fd); + OpenCommandPipe (); +} + + +ConnectionFileDescriptor::~ConnectionFileDescriptor () +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", this); + Disconnect (NULL); + CloseCommandPipe (); +} + +void +ConnectionFileDescriptor::OpenCommandPipe () +{ + CloseCommandPipe(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + // Make the command file descriptor here: + int filedes[2]; + int result = pipe (filedes); + if (result != 0) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor () - could not make pipe: %s", + this, + strerror(errno)); + } + else + { + m_pipe_read = filedes[0]; + m_pipe_write = filedes[1]; + } +} + +void +ConnectionFileDescriptor::CloseCommandPipe () +{ + if (m_pipe_read != -1) + { + close (m_pipe_read); + m_pipe_read = -1; + } + + if (m_pipe_write != -1) + { + close (m_pipe_write); + m_pipe_write = -1; + } +} + +bool +ConnectionFileDescriptor::IsConnected () const +{ + return m_fd_send >= 0 || m_fd_recv >= 0; +} + +ConnectionStatus +ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) +{ + Mutex::Locker locker (m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Connect (url = '%s')", this, s); + + OpenCommandPipe(); + + if (s && s[0]) + { + char *end = NULL; + if (strstr(s, "listen://")) + { + // listen://HOST:PORT + unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); + return SocketListen (listen_port, error_ptr); + } + else if (strstr(s, "unix-accept://")) + { + // unix://SOCKNAME + return NamedSocketAccept (s + strlen("unix-accept://"), error_ptr); + } + else if (strstr(s, "connect://")) + { + return ConnectTCP (s + strlen("connect://"), error_ptr); + } + else if (strstr(s, "tcp-connect://")) + { + return ConnectTCP (s + strlen("tcp-connect://"), error_ptr); + } + else if (strstr(s, "udp://")) + { + return ConnectUDP (s + strlen("udp://"), error_ptr); + } + else if (strstr(s, "fd://")) + { + // Just passing a native file descriptor within this current process + // that is already opened (possibly from a service or other source). + s += strlen ("fd://"); + bool success = false; + m_fd_send = m_fd_recv = Args::StringToSInt32 (s, -1, 0, &success); + + if (success) + { + // We have what looks to be a valid file descriptor, but we + // should make sure it is. We currently are doing this by trying to + // get the flags from the file descriptor and making sure it + // isn't a bad fd. + errno = 0; + int flags = ::fcntl (m_fd_send, F_GETFL, 0); + if (flags == -1 || errno == EBADF) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s); + m_fd_send = m_fd_recv = -1; + return eConnectionStatusError; + } + else + { + // Try and get a socket option from this file descriptor to + // see if this is a socket and set m_is_socket accordingly. + int resuse; + bool is_socket = GetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, resuse) == 0; + if (is_socket) + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + // Don't take ownership of a file descriptor that gets passed + // to us since someone else opened the file descriptor and + // handed it to us. + // TODO: Since are using a URL to open connection we should + // eventually parse options using the web standard where we + // have "fd://123?opt1=value;opt2=value" and we can have an + // option be "owns=1" or "owns=0" or something like this to + // allow us to specify this. For now, we assume we must + // assume we don't own it. + m_should_close_fd = false; + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s); + m_fd_send = m_fd_recv = -1; + return eConnectionStatusError; + } + else if (strstr(s, "file://")) + { + // file:///PATH + const char *path = s + strlen("file://"); + do + { + m_fd_send = m_fd_recv = ::open (path, O_RDWR); + } while (m_fd_send == -1 && errno == EINTR); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + if (::isatty(m_fd_send)) + { + // Set up serial terminal emulation + struct termios options; + ::tcgetattr (m_fd_send, &options); + + // Set port speed to maximum + ::cfsetospeed (&options, B115200); + ::cfsetispeed (&options, B115200); + + // Raw input, disable echo and signals + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + // Make sure only one character is needed to return from a read + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 0; + + ::tcsetattr (m_fd_send, TCSANOW, &options); + } + + int flags = ::fcntl (m_fd_send, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl (m_fd_send, F_SETFL, flags); + } + } + m_should_close_fd = true; + return eConnectionStatusSuccess; + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect (Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", this); + + ConnectionStatus status = eConnectionStatusSuccess; + + if (m_fd_send < 0 && m_fd_recv < 0) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", this); + return eConnectionStatusSuccess; + } + + // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely + // because somebody is doing a blocking read on our file descriptor. If that's the case, + // then send the "q" char to the command file channel so the read will wake up and the connection + // will then know to shut down. + + m_shutting_down = true; + + Mutex::Locker locker; + bool got_lock= locker.TryLock (m_mutex); + + if (!got_lock) + { + if (m_pipe_write != -1 ) + { + write (m_pipe_write, "q", 1); + close (m_pipe_write); + m_pipe_write = -1; + } + locker.Lock (m_mutex); + } + + if (m_should_close_fd == true) + { + if (m_fd_send == m_fd_recv) + { + status = Close (m_fd_send, error_ptr); + } + else + { + // File descriptors are the different, close both if needed + if (m_fd_send >= 0) + status = Close (m_fd_send, error_ptr); + if (m_fd_recv >= 0) + { + ConnectionStatus recv_status = Close (m_fd_recv, error_ptr); + if (status == eConnectionStatusSuccess) + status = recv_status; + } + } + } + + // Now set all our descriptors to invalid values. + + m_fd_send = m_fd_recv = -1; + + if (status != eConnectionStatusSuccess) + { + + return status; + } + + m_shutting_down = false; + return eConnectionStatusSuccess; +} + +size_t +ConnectionFileDescriptor::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ")...", + this, m_fd_recv, dst, (uint64_t)dst_len); + + Mutex::Locker locker; + bool got_lock = locker.TryLock (m_mutex); + if (!got_lock) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", + this); + if (error_ptr) + error_ptr->SetErrorString ("failed to get the connection lock for read."); + + status = eConnectionStatusTimedOut; + return 0; + } + else if (m_shutting_down) + return eConnectionStatusError; + + ssize_t bytes_read = 0; + + status = BytesAvailable (timeout_usec, error_ptr); + if (status == eConnectionStatusSuccess) + { + do + { + bytes_read = ::read (m_fd_recv, dst, dst_len); + } while (bytes_read < 0 && errno == EINTR); + } + + if (status != eConnectionStatusSuccess) + return 0; + + Error error; + if (bytes_read == 0) + { + error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. + status = eConnectionStatusEndOfFile; + } + else if (bytes_read < 0) + { + error.SetErrorToErrno(); + } + else + { + error.Clear(); + } + + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ") => %" PRIi64 ", error = %s", + this, + m_fd_recv, + dst, + (uint64_t)dst_len, + (int64_t)bytes_read, + error.AsCString()); + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + uint32_t error_value = error.GetError(); + switch (error_value) + { + case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. + if (m_fd_recv_type == eFDTypeSocket || m_fd_recv_type == eFDTypeSocketUDP) + status = eConnectionStatusTimedOut; + else + status = eConnectionStatusSuccess; + return 0; + + case EFAULT: // Buf points outside the allocated address space. + case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. + case EINVAL: // The pointer associated with fildes was negative. + case EIO: // An I/O error occurred while reading from the file system. + // The process group is orphaned. + // The file is a regular file, nbyte is greater than 0, + // the starting position is before the end-of-file, and + // the starting position is greater than or equal to the + // offset maximum established for the open file + // descriptor associated with fildes. + case EISDIR: // An attempt is made to read a directory. + case ENOBUFS: // An attempt to allocate a memory buffer fails. + case ENOMEM: // Insufficient memory is available. + status = eConnectionStatusError; + break; // Break to close.... + + case ENOENT: // no such file or directory + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + case ENXIO: // An action is requested of a device that does not exist.. + // A requested action cannot be performed by the device. + case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. + status = eConnectionStatusTimedOut; + return 0; + } + + return 0; + } + return bytes_read; +} + +size_t +ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", this, src, (uint64_t)src_len); + + if (!IsConnected ()) + { + if (error_ptr) + error_ptr->SetErrorString("not connected"); + status = eConnectionStatusNoConnection; + return 0; + } + + + Error error; + + ssize_t bytes_sent = 0; + + switch (m_fd_send_type) + { + case eFDTypeFile: // Other FD requireing read/write + do + { + bytes_sent = ::write (m_fd_send, src, src_len); + } while (bytes_sent < 0 && errno == EINTR); + break; + + case eFDTypeSocket: // Socket requiring send/recv + do + { + bytes_sent = ::send (m_fd_send, src, src_len, 0); + } while (bytes_sent < 0 && errno == EINTR); + break; + + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + assert (m_udp_send_sockaddr.GetFamily() != 0); + do + { + bytes_sent = ::sendto (m_fd_send, + src, + src_len, + 0, + m_udp_send_sockaddr, + m_udp_send_sockaddr.GetLength()); + } while (bytes_sent < 0 && errno == EINTR); + break; + } + + if (bytes_sent < 0) + error.SetErrorToErrno (); + else + error.Clear (); + + if (log) + { + switch (m_fd_send_type) + { + case eFDTypeFile: // Other FD requireing read/write + log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %" PRIu64 ") => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + + case eFDTypeSocket: // Socket requiring send/recv + log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + log->Printf ("%p ConnectionFileDescriptor::Write() ::sendto (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + } + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... + } + + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + + + +#if defined(__APPLE__) + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// - this Apple specific version allows for unlimited fds in the fd_sets by +// setting the _DARWIN_UNLIMITED_SELECT define prior to including the +// required header files. + +// CONS: +// - Darwin only + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + tv_ptr = NULL; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds (timeout_usec); + tv = time_value.GetAsTimeVal(); + tv_ptr = &tv; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + if (data_fd >= 0) + { + const bool have_pipe_fd = pipe_fd >= 0; + + while (data_fd == m_fd_recv) + { + const int nfds = std::max<int>(data_fd, pipe_fd) + 1; + llvm::SmallVector<fd_set, 1> read_fds; + read_fds.resize((nfds/FD_SETSIZE) + 1); + for (size_t i=0; i<read_fds.size(); ++i) + FD_ZERO (&read_fds[i]); + // FD_SET doesn't bounds check, it just happily walks off the end + // but we have taken care of making the extra storage with our + // SmallVector of fd_set objects + FD_SET (data_fd, read_fds.data()); + if (have_pipe_fd) + FD_SET (pipe_fd, read_fds.data()); + + Error error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, pipe_fd, tv_ptr); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, tv_ptr); + } + + const int num_set_fds = ::select (nfds, read_fds.data(), NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, pipe_fd, tv_ptr, num_set_fds, error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, tv_ptr, num_set_fds, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + // FD_ISSET is happy to deal with a something larger than + // a single fd_set. + if (FD_ISSET(data_fd, read_fds.data())) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, read_fds.data())) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#else + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// CONS: +// - only supports file descriptors up to FD_SETSIZE. This implementation +// will assert if it runs into that hard limit to let users know that +// another ConnectionFileDescriptor::BytesAvailable() should be used +// or a new version of ConnectionFileDescriptor::BytesAvailable() should +// be written for the system that is running into the limitations. MacOSX +// uses kqueues, and there is a poll() based implementation below. + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + tv_ptr = NULL; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds (timeout_usec); + tv = time_value.GetAsTimeVal(); + tv_ptr = &tv; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + if (data_fd >= 0) + { + // If this assert fires off on MacOSX, we will need to switch to using + // libdispatch to read from file descriptors because poll() is causing + // kernel panics and if we exceed FD_SETSIZE we will have no choice... + assert (data_fd < FD_SETSIZE); + + const bool have_pipe_fd = pipe_fd >= 0; + + if (have_pipe_fd) + { + assert (pipe_fd < FD_SETSIZE); + } + + while (data_fd == m_fd_recv) + { + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (data_fd, &read_fds); + if (have_pipe_fd) + FD_SET (pipe_fd, &read_fds); + + const int nfds = std::max<int>(data_fd, pipe_fd) + 1; + + Error error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, pipe_fd, tv_ptr); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, tv_ptr); + } + + const int num_set_fds = ::select (nfds, &read_fds, NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, pipe_fd, tv_ptr, num_set_fds, error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, tv_ptr, num_set_fds, error.AsCString()); + } + + if (error_ptr) + *error_ptr = error; + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (FD_ISSET(data_fd, &read_fds)) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, &read_fds)) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#endif + +#if 0 +#include <poll.h> + +// This ConnectionFileDescriptor::BytesAvailable() uses poll(). poll() should NOT +// be used on MacOSX as it has all sorts of restrictions on the types of file descriptors +// that it doesn't support. +// +// There may be some systems that properly support poll() that could use this +// implementation. I will let each system opt into this on their own. +// +// PROS: +// - no restrictions on the fd value that is used +// CONS: +// - varies wildly from platform to platform in its implementation restrictions + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + // Don't need to take the mutex here separately since we are only called from Read. If we + // ever get used more generally we will need to lock here as well. + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + int timeout_msec = 0; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + timeout_msec = -1; + } + else if (timeout_usec == 0) + { + // Return immediately, don't wait + timeout_msec = 0; + } + else + { + // Convert usec to msec + timeout_msec = (timeout_usec + 999) / 1000; + } + + // Make a copy of the file descriptors to make sure we don't + // have another thread change these values out from under us + // and cause problems in the loop below where like in FS_SET() + const int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + // Make sure the file descriptor can be used with select as it + // must be in range + if (data_fd >= 0) + { + const bool have_pipe_fd = pipe_fd >= 0; + struct pollfd fds[2] = + { + { data_fd, POLLIN, 0 }, + { pipe_fd, POLLIN, 0 } + }; + const int nfds = have_pipe_fd ? 2 : 1; + Error error; + while (data_fd == m_fd_recv) + { + const int num_set_fds = ::poll (fds, nfds, timeout_msec); + + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (error_ptr) + *error_ptr = error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN},{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n", + this, + data_fd, + pipe_fd, + nfds, + timeout_msec, + num_set_fds, + error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n", + this, + data_fd, + nfds, + timeout_msec, + num_set_fds, + error.AsCString()); + } + + if (error.Fail()) + { + switch (error.GetError()) + { + case EBADF: // One of the descriptor sets specified an invalid descriptor. + return eConnectionStatusLostConnection; + + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error + return eConnectionStatusError; + + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit + // expired and before any of the selected events + // occurred. + break; // Lets keep reading to until we timeout + } + } + else if (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (fds[0].revents & POLLIN) + return eConnectionStatusSuccess; + if (fds[1].revents & POLLIN) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#endif + +ConnectionStatus +ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + bool success = true; + // Avoid taking a lock if we can + if (fd >= 0) + { + Mutex::Locker locker (m_mutex); + // Check the FD after the lock is taken to ensure only one thread + // can get into the close scope below + if (fd >= 0) + { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Close (fd = %i)", this,fd); + + success = ::close (fd) == 0; + // A reference to a FD was passed in, set it to an invalid value + fd = -1; + if (!success && error_ptr) + { + // Only set the error if we have been asked to since something else + // might have caused us to try and shut down the connection and may + // have already set the error. + error_ptr->SetErrorToErrno(); + } + } + } + if (success) + return eConnectionStatusSuccess; + else + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) +{ + ConnectionStatus result = eConnectionStatusError; + struct sockaddr_un saddr_un; + + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + + int listen_socket = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (listen_socket == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; +#if defined(__APPLE__) || defined(__FreeBSD__) + saddr_un.sun_len = SUN_LEN (&saddr_un); +#endif + + if (::bind (listen_socket, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0) + { + if (::listen (listen_socket, 5) == 0) + { + m_fd_send = m_fd_recv = ::accept (listen_socket, NULL, 0); + if (m_fd_send > 0) + { + m_should_close_fd = true; + + if (error_ptr) + error_ptr->Clear(); + result = eConnectionStatusSuccess; + } + } + } + + if (result != eConnectionStatusSuccess) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + } + // We are done with the listen port + Close (listen_socket, NULL); + return result; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) +{ + Disconnect (NULL); + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + + // Open the socket that was passed in as an option + struct sockaddr_un saddr_un; + m_fd_send = m_fd_recv = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; +#if defined(__APPLE__) || defined(__FreeBSD__) + saddr_un.sun_len = SUN_LEN (&saddr_un); +#endif + + if (::connect (m_fd_send, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::SocketListen (port = %i)", this, listen_port_num); + + Disconnect (NULL); + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_port == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + // enable local address reuse + SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + + SocketAddress localhost; + if (localhost.SetToLocalhost (AF_INET, listen_port_num)) + { + int err = ::bind (listen_port, localhost, localhost.GetLength()); + if (err == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + + err = ::listen (listen_port, 1); + if (err == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + + m_fd_send = m_fd_recv = ::accept (listen_port, NULL, 0); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + } + + // We are done with the listen port + Close (listen_port, NULL); + + m_should_close_fd = true; + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectTCP (const char *host_and_port, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectTCP (host/port = %s)", this, host_and_port); + Disconnect (NULL); + + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + return eConnectionStatusError; + + // Create the socket + m_fd_send = m_fd_recv = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + m_should_close_fd = true; + + // Enable local address reuse + SetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (port); + + int inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); + + if (inet_pton_result <= 0) + { + struct hostent *host_entry = gethostbyname (host_str.c_str()); + if (host_entry) + host_str = ::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); + inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); + if (inet_pton_result <= 0) + { + + if (error_ptr) + { + if (inet_pton_result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str()); + } + Disconnect (NULL); + + return eConnectionStatusError; + } + } + + if (-1 == ::connect (m_fd_send, (const struct sockaddr *)&sa, sizeof(sa))) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + + return eConnectionStatusError; + } + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectUDP (host/port = %s)", this, host_and_port); + Disconnect (NULL); + + m_fd_send_type = m_fd_recv_type = eFDTypeSocketUDP; + + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + return eConnectionStatusError; + + // Setup the receiving end of the UDP connection on this localhost + // on port zero. After we bind to port zero we can read the port. + m_fd_recv = ::socket (AF_INET, SOCK_DGRAM, 0); + if (m_fd_recv == -1) + { + // Socket creation failed... + if (error_ptr) + error_ptr->SetErrorToErrno(); + } + else + { + // Socket was created, now lets bind to the requested port + SocketAddress addr; + addr.SetToLocalhost (AF_INET, 0); + + if (::bind (m_fd_recv, addr, addr.GetLength()) == -1) + { + // Bind failed... + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + } + } + + if (m_fd_recv == -1) + return eConnectionStatusError; + + // At this point we have setup the recieve port, now we need to + // setup the UDP send socket + + struct addrinfo hints; + struct addrinfo *service_info_list = NULL; + + ::memset (&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + int err = ::getaddrinfo (host_str.c_str(), port_str.c_str(), &hints, &service_info_list); + if (err != 0) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)", + host_str.c_str(), + port_str.c_str(), + err, + gai_strerror(err)); + Disconnect (NULL); + return eConnectionStatusError; + } + + for (struct addrinfo *service_info_ptr = service_info_list; + service_info_ptr != NULL; + service_info_ptr = service_info_ptr->ai_next) + { + m_fd_send = ::socket (service_info_ptr->ai_family, + service_info_ptr->ai_socktype, + service_info_ptr->ai_protocol); + + if (m_fd_send != -1) + { + m_udp_send_sockaddr = service_info_ptr; + break; + } + else + continue; + } + + :: freeaddrinfo (service_info_list); + + if (m_fd_send == -1) + { + Disconnect (NULL); + return eConnectionStatusError; + } + + if (error_ptr) + error_ptr->Clear(); + + m_should_close_fd = true; + return eConnectionStatusSuccess; +} + +#if defined(__MINGW32__) || defined(__MINGW64__) +typedef const char * set_socket_option_arg_type; +typedef char * get_socket_option_arg_type; +#else // #if defined(__MINGW32__) || defined(__MINGW64__) +typedef const void * set_socket_option_arg_type; +typedef void * get_socket_option_arg_type; +#endif // #if defined(__MINGW32__) || defined(__MINGW64__) + +int +ConnectionFileDescriptor::GetSocketOption(int fd, int level, int option_name, int &option_value) +{ + get_socket_option_arg_type option_value_p = static_cast<get_socket_option_arg_type>(&option_value); + socklen_t option_value_size = sizeof(int); + return ::getsockopt(fd, level, option_name, option_value_p, &option_value_size); +} + +int +ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) +{ + set_socket_option_arg_type option_value_p = static_cast<get_socket_option_arg_type>(&option_value); + return ::setsockopt(fd, level, option_name, option_value_p, sizeof(option_value)); +} + +bool +ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec) +{ + switch (m_fd_recv_type) + { + case eFDTypeFile: // Other FD requireing read/write + break; + + case eFDTypeSocket: // Socket requiring send/recv + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + { + // Check in case timeout for m_fd has already been set to this value + if (timeout_usec == m_socket_timeout_usec) + return true; + //printf ("ConnectionFileDescriptor::SetSocketReceiveTimeout (timeout_usec = %u)\n", timeout_usec); + + struct timeval timeout; + if (timeout_usec == UINT32_MAX) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + else if (timeout_usec == 0) + { + // Sending in zero does an infinite timeout, so set this as low + // as we can go to get an effective zero timeout... + timeout.tv_sec = 0; + timeout.tv_usec = 1; + } + else + { + timeout.tv_sec = timeout_usec / TimeValue::MicroSecPerSec; + timeout.tv_usec = timeout_usec % TimeValue::MicroSecPerSec; + } + if (::setsockopt (m_fd_recv, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == 0) + { + m_socket_timeout_usec = timeout_usec; + return true; + } + } + } + return false; +} + +in_port_t +ConnectionFileDescriptor::GetSocketPort (int fd) +{ + // We bound to port zero, so we need to figure out which port we actually bound to + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + + return 0; +} + +// If the read file descriptor is a socket, then return +// the port number that is being used by the socket. +in_port_t +ConnectionFileDescriptor::GetReadPort () const +{ + return ConnectionFileDescriptor::GetSocketPort (m_fd_recv); +} + +// If the write file descriptor is a socket, then return +// the port number that is being used by the socket. +in_port_t +ConnectionFileDescriptor::GetWritePort () const +{ + return ConnectionFileDescriptor::GetSocketPort (m_fd_send); +} + + diff --git a/source/Core/ConnectionMachPort.cpp b/source/Core/ConnectionMachPort.cpp new file mode 100644 index 0000000..ca818d4 --- /dev/null +++ b/source/Core/ConnectionMachPort.cpp @@ -0,0 +1,323 @@ +//===-- ConnectionMachPort.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#if defined(__APPLE__) + +#include "lldb/Core/ConnectionMachPort.h" + +// C Includes +#include <servers/bootstrap.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +struct MessageType +{ + mach_msg_header_t head; + ConnectionMachPort::PayloadType payload; +}; + + + +ConnectionMachPort::ConnectionMachPort () : + Connection(), + m_task(mach_task_self()), + m_port(MACH_PORT_TYPE_NONE) +{ +} + +ConnectionMachPort::~ConnectionMachPort () +{ + Disconnect (NULL); +} + +bool +ConnectionMachPort::IsConnected () const +{ + return m_port != MACH_PORT_TYPE_NONE; +} + +ConnectionStatus +ConnectionMachPort::Connect (const char *s, Error *error_ptr) +{ + if (IsConnected()) + { + if (error_ptr) + error_ptr->SetErrorString ("already connected"); + return eConnectionStatusError; + } + + if (s == NULL || s[0] == '\0') + { + if (error_ptr) + error_ptr->SetErrorString ("empty connect URL"); + return eConnectionStatusError; + } + + ConnectionStatus status = eConnectionStatusError; + + if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://"))) + { + s += strlen("bootstrap-checkin://"); + + if (*s) + { + status = BootstrapCheckIn (s, error_ptr); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("bootstrap port name is empty"); + } + } + else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://"))) + { + s += strlen("bootstrap-lookup://"); + if (*s) + { + status = BootstrapLookup (s, error_ptr); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("bootstrap port name is empty"); + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + } + + + if (status == eConnectionStatusSuccess) + { + if (error_ptr) + error_ptr->Clear(); + } + else + { + Disconnect(NULL); + } + + return status; +} + +ConnectionStatus +ConnectionMachPort::BootstrapCheckIn (const char *port, Error *error_ptr) +{ + mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; + + /* Getting bootstrap server port */ + kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + if (kret == KERN_SUCCESS) + { + name_t port_name; + int len = snprintf(port_name, sizeof(port_name), "%s", port); + if (len < sizeof(port_name)) + { + kret = ::bootstrap_check_in (bootstrap_port, + port_name, + &m_port); + } + else + { + Disconnect(NULL); + if (error_ptr) + error_ptr->SetErrorString ("bootstrap is too long"); + return eConnectionStatusError; + } + } + + if (kret != KERN_SUCCESS) + { + Disconnect(NULL); + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + return eConnectionStatusError; + } + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionMachPort::BootstrapLookup (const char *port, + Error *error_ptr) +{ + name_t port_name; + + if (port && port[0]) + { + if (::snprintf (port_name, sizeof (port_name), "%s", port) >= sizeof (port_name)) + { + if (error_ptr) + error_ptr->SetErrorString ("port netname is too long"); + return eConnectionStatusError; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("empty port netname"); + return eConnectionStatusError; + } + + mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; + + /* Getting bootstrap server port */ + kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + if (kret == KERN_SUCCESS) + { + kret = ::bootstrap_look_up (bootstrap_port, + port_name, + &m_port); + } + + if (kret != KERN_SUCCESS) + { + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + return eConnectionStatusError; + } + + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionMachPort::Disconnect (Error *error_ptr) +{ + kern_return_t kret; + + // TODO: verify if we need to netname_check_out for + // either or both + if (m_port != MACH_PORT_TYPE_NONE) + { + kret = ::mach_port_deallocate (m_task, m_port); + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + m_port = MACH_PORT_TYPE_NONE; + } + + return eConnectionStatusSuccess; +} + +size_t +ConnectionMachPort::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + PayloadType payload; + + kern_return_t kret = Receive (payload); + if (kret == KERN_SUCCESS) + { + memcpy (dst, payload.data, payload.data_length); + status = eConnectionStatusSuccess; + return payload.data_length; + } + + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + status = eConnectionStatusError; + return 0; +} + +size_t +ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + PayloadType payload; + payload.command = 0; + payload.data_length = src_len; + const size_t max_payload_size = sizeof(payload.data); + if (src_len > max_payload_size) + payload.data_length = max_payload_size; + memcpy (payload.data, src, payload.data_length); + + if (Send (payload) == KERN_SUCCESS) + { + status = eConnectionStatusSuccess; + return payload.data_length; + } + status = eConnectionStatusError; + return 0; +} + +ConnectionStatus +ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + return eConnectionStatusLostConnection; +} + +kern_return_t +ConnectionMachPort::Send (const PayloadType &payload) +{ + struct MessageType message; + + /* (i) Form the message : */ + + /* (i.a) Fill the header fields : */ + message.head.msgh_bits = MACH_MSGH_BITS_REMOTE (MACH_MSG_TYPE_MAKE_SEND) | + MACH_MSGH_BITS_OTHER (MACH_MSGH_BITS_COMPLEX); + message.head.msgh_size = sizeof(MessageType); + message.head.msgh_local_port = MACH_PORT_NULL; + message.head.msgh_remote_port = m_port; + + /* (i.b) Explain the message type ( an integer ) */ + // message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32; + // message.type.msgt_size = 32; + // message.type.msgt_number = 1; + // message.type.msgt_inline = TRUE; + // message.type.msgt_longform = FALSE; + // message.type.msgt_deallocate = FALSE; + /* message.type.msgt_unused = 0; */ /* not needed, I think */ + + /* (i.c) Fill the message with the given integer : */ + message.payload = payload; + + /* (ii) Send the message : */ + kern_return_t kret = ::mach_msg (&message.head, + MACH_SEND_MSG, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + return kret; +} + +kern_return_t +ConnectionMachPort::Receive (PayloadType &payload) +{ + MessageType message; + message.head.msgh_size = sizeof(MessageType); + + kern_return_t kret = ::mach_msg (&message.head, + MACH_RCV_MSG, + 0, + sizeof(MessageType), + m_port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (kret == KERN_SUCCESS) + payload = message.payload; + + return kret; +} + + +#endif // #if defined(__APPLE__) diff --git a/source/Core/ConnectionSharedMemory.cpp b/source/Core/ConnectionSharedMemory.cpp new file mode 100644 index 0000000..625f17a --- /dev/null +++ b/source/Core/ConnectionSharedMemory.cpp @@ -0,0 +1,131 @@ +//===-- ConnectionSharedMemory.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ConnectionSharedMemory.h" + +// C Includes +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <sys/file.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +ConnectionSharedMemory::ConnectionSharedMemory () : + Connection(), + m_name(), + m_fd (-1), + m_mmap() +{ +} + +ConnectionSharedMemory::~ConnectionSharedMemory () +{ + Disconnect (NULL); +} + +bool +ConnectionSharedMemory::IsConnected () const +{ + return m_fd >= 0; +} + +ConnectionStatus +ConnectionSharedMemory::Connect (const char *s, Error *error_ptr) +{ +// if (s && s[0]) +// { +// if (strstr(s, "shm-create://")) +// { +// } +// else if (strstr(s, "shm-connect://")) +// { +// } +// if (error_ptr) +// error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); +// return eConnectionStatusError; +// } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionSharedMemory::Disconnect (Error *error_ptr) +{ + m_mmap.Clear(); + if (!m_name.empty()) + { + shm_unlink (m_name.c_str()); + m_name.clear(); + } + return eConnectionStatusSuccess; +} + +size_t +ConnectionSharedMemory::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + status = eConnectionStatusSuccess; + return 0; +} + +size_t +ConnectionSharedMemory::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + status = eConnectionStatusSuccess; + return 0; +} + +ConnectionStatus +ConnectionSharedMemory::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionSharedMemory::Open (bool create, const char *name, size_t size, Error *error_ptr) +{ + if (m_fd != -1) + { + if (error_ptr) + error_ptr->SetErrorString("already open"); + return eConnectionStatusError; + } + + m_name.assign (name); + int oflag = O_RDWR; + if (create) + oflag |= O_CREAT; + m_fd = ::shm_open (m_name.c_str(), oflag, S_IRUSR|S_IWUSR); + + if (create) + ::ftruncate (m_fd, size); + + if (m_mmap.MemoryMapFromFileDescriptor(m_fd, 0, size, true, false) == size) + return eConnectionStatusSuccess; + + Disconnect(NULL); + return eConnectionStatusError; +} + diff --git a/source/Core/ConstString.cpp b/source/Core/ConstString.cpp new file mode 100644 index 0000000..8751694 --- /dev/null +++ b/source/Core/ConstString.cpp @@ -0,0 +1,342 @@ +//===-- ConstString.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" +#include "llvm/ADT/StringMap.h" + +using namespace lldb_private; + + +class Pool +{ +public: + typedef const char * StringPoolValueType; + typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator> StringPool; + typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType; + + //------------------------------------------------------------------ + // Default constructor + // + // Initialize the member variables and create the empty string. + //------------------------------------------------------------------ + Pool () : + m_mutex (Mutex::eMutexTypeRecursive), + m_string_map () + { + } + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~Pool () + { + } + + + static StringPoolEntryType & + GetStringMapEntryFromKeyData (const char *keyData) + { + char *ptr = const_cast<char*>(keyData) - sizeof (StringPoolEntryType); + return *reinterpret_cast<StringPoolEntryType*>(ptr); + } + + size_t + GetConstCStringLength (const char *ccstr) const + { + if (ccstr) + { + const StringPoolEntryType&entry = GetStringMapEntryFromKeyData (ccstr); + return entry.getKey().size(); + } + return 0; + } + + StringPoolValueType + GetMangledCounterpart (const char *ccstr) const + { + if (ccstr) + return GetStringMapEntryFromKeyData (ccstr).getValue(); + return 0; + } + + bool + SetMangledCounterparts (const char *key_ccstr, const char *value_ccstr) + { + if (key_ccstr && value_ccstr) + { + GetStringMapEntryFromKeyData (key_ccstr).setValue(value_ccstr); + GetStringMapEntryFromKeyData (value_ccstr).setValue(key_ccstr); + return true; + } + return false; + } + + const char * + GetConstCString (const char *cstr) + { + if (cstr) + return GetConstCStringWithLength (cstr, strlen (cstr)); + return NULL; + } + + const char * + GetConstCStringWithLength (const char *cstr, size_t cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr, cstr_len); + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + return entry.getKeyData(); + } + return NULL; + } + + const char * + GetConstCStringWithStringRef (const llvm::StringRef &string_ref) + { + if (string_ref.data()) + { + Mutex::Locker locker (m_mutex); + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + return entry.getKeyData(); + } + return NULL; + } + + const char * + GetConstCStringAndSetMangledCounterPart (const char *demangled_cstr, const char *mangled_ccstr) + { + if (demangled_cstr) + { + Mutex::Locker locker (m_mutex); + // Make string pool entry with the mangled counterpart already set + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (llvm::StringRef (demangled_cstr), mangled_ccstr); + + // Extract the const version of the demangled_cstr + const char *demangled_ccstr = entry.getKeyData(); + // Now assign the demangled const string as the counterpart of the + // mangled const string... + GetStringMapEntryFromKeyData (mangled_ccstr).setValue(demangled_ccstr); + // Return the constant demangled C string + return demangled_ccstr; + } + return NULL; + } + + const char * + GetConstTrimmedCStringWithLength (const char *cstr, size_t cstr_len) + { + if (cstr) + { + const size_t trimmed_len = std::min<size_t> (strlen (cstr), cstr_len); + return GetConstCStringWithLength (cstr, trimmed_len); + } + return NULL; + } + + //------------------------------------------------------------------ + // Return the size in bytes that this object and any items in its + // collection of uniqued strings + data count values takes in + // memory. + //------------------------------------------------------------------ + size_t + MemorySize() const + { + Mutex::Locker locker (m_mutex); + size_t mem_size = sizeof(Pool); + const_iterator end = m_string_map.end(); + for (const_iterator pos = m_string_map.begin(); pos != end; ++pos) + { + mem_size += sizeof(StringPoolEntryType) + pos->getKey().size(); + } + return mem_size; + } + +protected: + //------------------------------------------------------------------ + // Typedefs + //------------------------------------------------------------------ + typedef StringPool::iterator iterator; + typedef StringPool::const_iterator const_iterator; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + mutable Mutex m_mutex; + StringPool m_string_map; +}; + +//---------------------------------------------------------------------- +// Frameworks and dylibs aren't supposed to have global C++ +// initializers so we hide the string pool in a static function so +// that it will get initialized on the first call to this static +// function. +// +// Note, for now we make the string pool a pointer to the pool, because +// we can't guarantee that some objects won't get destroyed after the +// global destructor chain is run, and trying to make sure no destructors +// touch ConstStrings is difficult. So we leak the pool instead. +// +// FIXME: If we are going to keep it this way we should come up with some +// abstraction to "pthread_once" so we don't have to check the pointer +// every time. +//---------------------------------------------------------------------- +static Pool & +StringPool() +{ + static Mutex g_pool_initialization_mutex; + static Pool *g_string_pool = NULL; + + if (g_string_pool == NULL) + { + Mutex::Locker initialization_locker(g_pool_initialization_mutex); + if (g_string_pool == NULL) + { + g_string_pool = new Pool(); + } + } + + return *g_string_pool; +} + +ConstString::ConstString (const char *cstr) : + m_string (StringPool().GetConstCString (cstr)) +{ +} + +ConstString::ConstString (const char *cstr, size_t cstr_len) : + m_string (StringPool().GetConstCStringWithLength (cstr, cstr_len)) +{ +} + +ConstString::ConstString (const llvm::StringRef &s) : + m_string (StringPool().GetConstCStringWithLength (s.data(), s.size())) +{ +} + +bool +ConstString::operator < (const ConstString& rhs) const +{ + if (m_string == rhs.m_string) + return false; + + llvm::StringRef lhs_string_ref (m_string, StringPool().GetConstCStringLength (m_string)); + llvm::StringRef rhs_string_ref (rhs.m_string, StringPool().GetConstCStringLength (rhs.m_string)); + + // If both have valid C strings, then return the comparison + if (lhs_string_ref.data() && rhs_string_ref.data()) + return lhs_string_ref < rhs_string_ref; + + // Else one of them was NULL, so if LHS is NULL then it is less than + return lhs_string_ref.data() == NULL; +} + +Stream& +lldb_private::operator << (Stream& s, const ConstString& str) +{ + const char *cstr = str.GetCString(); + if (cstr) + s << cstr; + + return s; +} + +size_t +ConstString::GetLength () const +{ + return StringPool().GetConstCStringLength (m_string); +} + +int +ConstString::Compare (const ConstString& lhs, const ConstString& rhs) +{ + // If the iterators are the same, this is the same string + register const char *lhs_cstr = lhs.m_string; + register const char *rhs_cstr = rhs.m_string; + if (lhs_cstr == rhs_cstr) + return 0; + if (lhs_cstr && rhs_cstr) + { + llvm::StringRef lhs_string_ref (lhs_cstr, StringPool().GetConstCStringLength (lhs_cstr)); + llvm::StringRef rhs_string_ref (rhs_cstr, StringPool().GetConstCStringLength (rhs_cstr)); + return lhs_string_ref.compare(rhs_string_ref); + } + + if (lhs_cstr) + return +1; // LHS isn't NULL but RHS is + else + return -1; // LHS is NULL but RHS isn't +} + +void +ConstString::Dump(Stream *s, const char *fail_value) const +{ + if (s) + { + const char *cstr = AsCString (fail_value); + if (cstr) + s->PutCString (cstr); + } +} + +void +ConstString::DumpDebug(Stream *s) const +{ + const char *cstr = GetCString (); + size_t cstr_len = GetLength(); + // Only print the parens if we have a non-NULL string + const char *parens = cstr ? "\"" : ""; + s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64, (int)sizeof(void*) * 2, this, parens, cstr, parens, (uint64_t)cstr_len); +} + +void +ConstString::SetCString (const char *cstr) +{ + m_string = StringPool().GetConstCString (cstr); +} + +void +ConstString::SetString (const llvm::StringRef &s) +{ + m_string = StringPool().GetConstCStringWithLength (s.data(), s.size()); +} + +void +ConstString::SetCStringWithMangledCounterpart (const char *demangled, const ConstString &mangled) +{ + m_string = StringPool().GetConstCStringAndSetMangledCounterPart (demangled, mangled.m_string); +} + +bool +ConstString::GetMangledCounterpart (ConstString &counterpart) const +{ + counterpart.m_string = StringPool().GetMangledCounterpart(m_string); + return counterpart; +} + +void +ConstString::SetCStringWithLength (const char *cstr, size_t cstr_len) +{ + m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len); +} + +void +ConstString::SetTrimmedCStringWithLength (const char *cstr, size_t cstr_len) +{ + m_string = StringPool().GetConstTrimmedCStringWithLength (cstr, cstr_len); +} + +size_t +ConstString::StaticMemorySize() +{ + // Get the size of the static string pool + return StringPool().MemorySize(); +} diff --git a/source/Core/DataBufferHeap.cpp b/source/Core/DataBufferHeap.cpp new file mode 100644 index 0000000..2c8a865 --- /dev/null +++ b/source/Core/DataBufferHeap.cpp @@ -0,0 +1,111 @@ +//===-- DataBufferHeap.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataBufferHeap.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap () : + m_data() +{ +} + +//---------------------------------------------------------------------- +// Initialize this class with "n" characters and fill the buffer +// with "ch". +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap (lldb::offset_t n, uint8_t ch) : + m_data() +{ + if (n < m_data.max_size()) + m_data.assign (n, ch); +} + +//---------------------------------------------------------------------- +// Initialize this class with a copy of the "n" bytes from the "bytes" +// buffer. +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap (const void *src, lldb::offset_t src_len) : + m_data() +{ + CopyData (src, src_len); +} + +//---------------------------------------------------------------------- +// Virtual destructor since this class inherits from a pure virtual +// base class. +//---------------------------------------------------------------------- +DataBufferHeap::~DataBufferHeap () +{ +} + +//---------------------------------------------------------------------- +// Return a pointer to the bytes owned by this object, or NULL if +// the object contains no bytes. +//---------------------------------------------------------------------- +uint8_t * +DataBufferHeap::GetBytes () +{ + if (m_data.empty()) + return NULL; + return &m_data[0]; +} + +//---------------------------------------------------------------------- +// Return a const pointer to the bytes owned by this object, or NULL +// if the object contains no bytes. +//---------------------------------------------------------------------- +const uint8_t * +DataBufferHeap::GetBytes () const +{ + if (m_data.empty()) + return NULL; + return &m_data[0]; +} + +//---------------------------------------------------------------------- +// Return the number of bytes this object currently contains. +//---------------------------------------------------------------------- +uint64_t +DataBufferHeap::GetByteSize () const +{ + return m_data.size(); +} + + +//---------------------------------------------------------------------- +// Sets the number of bytes that this object should be able to +// contain. This can be used prior to copying data into the buffer. +//---------------------------------------------------------------------- +uint64_t +DataBufferHeap::SetByteSize (uint64_t new_size) +{ + m_data.resize(new_size); + return m_data.size(); +} + +void +DataBufferHeap::CopyData (const void *src, uint64_t src_len) +{ + const uint8_t *src_u8 = (const uint8_t *)src; + if (src && src_len > 0) + m_data.assign (src_u8, src_u8 + src_len); + else + m_data.clear(); +} + +void +DataBufferHeap::Clear() +{ + buffer_t empty; + m_data.swap(empty); +} diff --git a/source/Core/DataBufferMemoryMap.cpp b/source/Core/DataBufferMemoryMap.cpp new file mode 100644 index 0000000..a4382a0 --- /dev/null +++ b/source/Core/DataBufferMemoryMap.cpp @@ -0,0 +1,258 @@ +//===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/Log.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +DataBufferMemoryMap::DataBufferMemoryMap() : + m_mmap_addr(NULL), + m_mmap_size(0), + m_data(NULL), + m_size(0) +{ +} + +//---------------------------------------------------------------------- +// Virtual destructor since this class inherits from a pure virtual +// base class. +//---------------------------------------------------------------------- +DataBufferMemoryMap::~DataBufferMemoryMap() +{ + Clear(); +} + +//---------------------------------------------------------------------- +// Return a pointer to the bytes owned by this object, or NULL if +// the object contains no bytes. +//---------------------------------------------------------------------- +uint8_t * +DataBufferMemoryMap::GetBytes() +{ + return m_data; +} + +//---------------------------------------------------------------------- +// Return a const pointer to the bytes owned by this object, or NULL +// if the object contains no bytes. +//---------------------------------------------------------------------- +const uint8_t * +DataBufferMemoryMap::GetBytes() const +{ + return m_data; +} + +//---------------------------------------------------------------------- +// Return the number of bytes this object currently contains. +//---------------------------------------------------------------------- +uint64_t +DataBufferMemoryMap::GetByteSize() const +{ + return m_size; +} + +//---------------------------------------------------------------------- +// Reverts this object to an empty state by unmapping any memory +// that is currently owned. +//---------------------------------------------------------------------- +void +DataBufferMemoryMap::Clear() +{ + if (m_mmap_addr != NULL) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); + if (log) + log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size); + ::munmap((void *)m_mmap_addr, m_mmap_size); + m_mmap_addr = NULL; + m_mmap_size = 0; + m_data = NULL; + m_size = 0; + } +} + +//---------------------------------------------------------------------- +// Memory map "length" bytes from "file" starting "offset" +// bytes into the file. If "length" is set to SIZE_MAX, then +// map as many bytes as possible. +// +// Returns the number of bytes mapped starting from the requested +// offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec, + lldb::offset_t offset, + lldb::offset_t length, + bool writeable) +{ + if (filespec != NULL) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i", + filespec->GetPath().c_str(), + offset, + length, + writeable); + } + char path[PATH_MAX]; + if (filespec->GetPath(path, sizeof(path))) + { + uint32_t options = File::eOpenOptionRead; + if (writeable) + options |= File::eOpenOptionWrite; + + File file; + Error error (file.Open(path, options)); + if (error.Success()) + { + const bool fd_is_file = true; + return MemoryMapFromFileDescriptor (file.GetDescriptor(), offset, length, writeable, fd_is_file); + } + } + } + // We should only get here if there was an error + Clear(); + return 0; +} + + +//---------------------------------------------------------------------- +// The file descriptor FD is assumed to already be opened as read only +// and the STAT structure is assumed to a valid pointer and already +// containing valid data from a call to stat(). +// +// Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into +// the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes +// as possible. +// +// RETURNS +// Number of bytes mapped starting from the requested offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, + lldb::offset_t offset, + lldb::offset_t length, + bool writeable, + bool fd_is_file) +{ + Clear(); + if (fd >= 0) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE)); + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", + fd, + offset, + length, + writeable, + fd_is_file); + } + struct stat stat; + if (::fstat(fd, &stat) == 0) + { + if (S_ISREG(stat.st_mode) && (stat.st_size > offset)) + { + const size_t max_bytes_available = stat.st_size - offset; + if (length == SIZE_MAX) + { + length = max_bytes_available; + } + else if (length > max_bytes_available) + { + // Cap the length if too much data was requested + length = max_bytes_available; + } + + if (length > 0) + { + int prot = PROT_READ; + if (writeable) + prot |= PROT_WRITE; + + int flags = MAP_PRIVATE; + if (fd_is_file) + flags |= MAP_FILE; + + m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset); + Error error; + + if (m_mmap_addr == (void*)-1) + { + error.SetErrorToErrno (); + if (error.GetError() == EINVAL) + { + // We may still have a shot at memory mapping if we align things correctly + size_t page_offset = offset % Host::GetPageSize(); + if (page_offset != 0) + { + m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset); + if (m_mmap_addr == (void*)-1) + { + // Failed to map file + m_mmap_addr = NULL; + } + else if (m_mmap_addr != NULL) + { + // We recovered and were able to memory map + // after we aligned things to page boundaries + + // Save the actual mmap'ed size + m_mmap_size = length + page_offset; + // Our data is at an offset into the the mapped data + m_data = m_mmap_addr + page_offset; + // Our pretend size is the size that was requestd + m_size = length; + } + } + } + if (error.GetError() == ENOMEM) + { + error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length); + } + } + else + { + // We were able to map the requested data in one chunk + // where our mmap and actual data are the same. + m_mmap_size = length; + m_data = m_mmap_addr; + m_size = length; + } + + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %zu, error = %s", + m_mmap_addr, m_mmap_size, error.AsCString()); + } + } + } + } + } + return GetByteSize (); +} diff --git a/source/Core/DataEncoder.cpp b/source/Core/DataEncoder.cpp new file mode 100644 index 0000000..92a9104 --- /dev/null +++ b/source/Core/DataEncoder.cpp @@ -0,0 +1,335 @@ +//===-- DataEncoder.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataEncoder.h" + +#include <assert.h> +#include <stddef.h> + +#include "llvm/Support/MathExtras.h" + +#include "lldb/Core/DataBuffer.h" +#include "lldb/Host/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +static inline void +WriteInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +{ + *(uint16_t *)(ptr + offset) = value; +} +static inline void +WriteInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +{ + *(uint32_t *)(ptr + offset) = value; +} + +static inline void +WriteInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +{ + *(uint64_t *)(ptr + offset) = value; +} + +static inline void +WriteSwappedInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +{ + *(uint16_t *)(ptr + offset) = llvm::ByteSwap_16(value); +} + +static inline void +WriteSwappedInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +{ + *(uint32_t *)(ptr + offset) = llvm::ByteSwap_32(value); +} + +static inline void +WriteSwappedInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +{ + *(uint64_t *)(ptr + offset) = llvm::ByteSwap_64(value); +} + +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder () : + m_start (NULL), + m_end (NULL), + m_byte_order(lldb::endian::InlHostByteOrder()), + m_addr_size (sizeof(void*)), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// This constructor allows us to use data that is owned by someone else. +// The data must stay around as long as this object is valid. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder (void* data, uint32_t length, ByteOrder endian, uint8_t addr_size) : + m_start ((uint8_t*)data), + m_end ((uint8_t*)data + length), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// Make a shared pointer reference to the shared data in "data_sp" and +// set the endian swapping setting to "swap", and the address size to +// "addr_size". The shared data reference will ensure the data lives +// as long as any DataEncoder objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder (const DataBufferSP& data_sp, ByteOrder endian, uint8_t addr_size) : + m_start (NULL), + m_end (NULL), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ + SetData (data_sp); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DataEncoder::~DataEncoder () +{ +} + +//------------------------------------------------------------------ +// Clears the object contents back to a default invalid state, and +// release any references to shared data that this object may +// contain. +//------------------------------------------------------------------ +void +DataEncoder::Clear () +{ + m_start = NULL; + m_end = NULL; + m_byte_order = lldb::endian::InlHostByteOrder(); + m_addr_size = sizeof(void*); + m_data_sp.reset(); +} + +//------------------------------------------------------------------ +// If this object contains shared data, this function returns the +// offset into that shared data. Else zero is returned. +//------------------------------------------------------------------ +size_t +DataEncoder::GetSharedDataOffset () const +{ + if (m_start != NULL) + { + const DataBuffer * data = m_data_sp.get(); + if (data != NULL) + { + const uint8_t * data_bytes = data->GetBytes(); + if (data_bytes != NULL) + { + assert(m_start >= data_bytes); + return m_start - data_bytes; + } + } + } + return 0; +} + +//---------------------------------------------------------------------- +// Set the data with which this object will extract from to data +// starting at BYTES and set the length of the data to LENGTH bytes +// long. The data is externally owned must be around at least as +// long as this object points to the data. No copy of the data is +// made, this object just refers to this data and can extract from +// it. If this object refers to any shared data upon entry, the +// reference to that data will be released. Is SWAP is set to true, +// any data extracted will be endian swapped. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::SetData (const void *bytes, uint32_t length, ByteOrder endian) +{ + m_byte_order = endian; + m_data_sp.reset(); + if (bytes == NULL || length == 0) + { + m_start = NULL; + m_end = NULL; + } + else + { + m_start = (uint8_t *)bytes; + m_end = m_start + length; + } + return GetByteSize(); +} + +//---------------------------------------------------------------------- +// Assign the data for this object to be a subrange of the shared +// data in "data_sp" starting "data_offset" bytes into "data_sp" +// and ending "data_length" bytes later. If "data_offset" is not +// a valid offset into "data_sp", then this object will contain no +// bytes. If "data_offset" is within "data_sp" yet "data_length" is +// too large, the length will be capped at the number of bytes +// remaining in "data_sp". A ref counted pointer to the data in +// "data_sp" will be made in this object IF the number of bytes this +// object refers to in greater than zero (if at least one byte was +// available starting at "data_offset") to ensure the data stays +// around as long as it is needed. The address size and endian swap +// settings will remain unchanged from their current settings. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::SetData (const DataBufferSP& data_sp, uint32_t data_offset, uint32_t data_length) +{ + m_start = m_end = NULL; + + if (data_length > 0) + { + m_data_sp = data_sp; + if (data_sp.get()) + { + const size_t data_size = data_sp->GetByteSize(); + if (data_offset < data_size) + { + m_start = data_sp->GetBytes() + data_offset; + const size_t bytes_left = data_size - data_offset; + // Cap the length of we asked for too many + if (data_length <= bytes_left) + m_end = m_start + data_length; // We got all the bytes we wanted + else + m_end = m_start + bytes_left; // Not all the bytes requested were available in the shared data + } + } + } + + uint32_t new_size = GetByteSize(); + + // Don't hold a shared pointer to the data buffer if we don't share + // any valid bytes in the shared buffer. + if (new_size == 0) + m_data_sp.reset(); + + return new_size; +} + +//---------------------------------------------------------------------- +// Extract a single unsigned char from the binary data and update +// the offset pointed to by "offset_ptr". +// +// RETURNS the byte that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::PutU8 (uint32_t offset, uint8_t value) +{ + if (ValidOffset(offset)) + { + m_start[offset] = value; + return offset + 1; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutU16 (uint32_t offset, uint16_t value) +{ + if (ValidOffsetForDataOfSize(offset, sizeof(value))) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + WriteSwappedInt16 (m_start, offset, value); + else + WriteInt16 (m_start, offset, value); + + return offset + sizeof (value); + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutU32 (uint32_t offset, uint32_t value) +{ + if (ValidOffsetForDataOfSize(offset, sizeof(value))) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + WriteSwappedInt32 (m_start, offset, value); + else + WriteInt32 (m_start, offset, value); + + return offset + sizeof (value); + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutU64 (uint32_t offset, uint64_t value) +{ + if (ValidOffsetForDataOfSize(offset, sizeof(value))) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + WriteSwappedInt64 (m_start, offset, value); + else + WriteInt64 (m_start, offset, value); + + return offset + sizeof (value); + } + return UINT32_MAX; +} + +//---------------------------------------------------------------------- +// Extract a single integer value from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted integer +// is specified by the "byte_size" argument. "byte_size" should have +// a value >= 1 and <= 8 since the return value is only 64 bits +// wide. Any "byte_size" values less than 1 or greater than 8 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataEncoder::PutMaxU64 (uint32_t offset, uint32_t byte_size, uint64_t value) +{ + switch (byte_size) + { + case 1: return PutU8 (offset, value); + case 2: return PutU16(offset, value); + case 4: return PutU32(offset, value); + case 8: return PutU64(offset, value); + default: + assert(!"GetMax64 unhandled case!"); + break; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutData (uint32_t offset, const void *src, uint32_t src_len) +{ + if (src == NULL || src_len == 0) + return offset; + + if (ValidOffsetForDataOfSize(offset, src_len)) + { + memcpy (m_start + offset, src, src_len); + return offset + src_len; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutAddress (uint32_t offset, lldb::addr_t addr) +{ + return PutMaxU64 (offset, GetAddressByteSize(), addr); +} + +uint32_t +DataEncoder::PutCString (uint32_t offset, const char *cstr) +{ + if (cstr) + return PutData (offset, cstr, strlen(cstr) + 1); + return UINT32_MAX; +} diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp new file mode 100644 index 0000000..518faeb --- /dev/null +++ b/source/Core/DataExtractor.cpp @@ -0,0 +1,2179 @@ +//===-- DataExtractor.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <stddef.h> + +#include <bitset> +#include <limits> +#include <sstream> +#include <string> + +#include "clang/AST/ASTContext.h" + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MathExtras.h" + + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UUID.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static inline uint16_t +ReadInt16(const unsigned char* ptr, offset_t offset) +{ + return *(uint16_t *)(ptr + offset); +} +static inline uint32_t +ReadInt32 (const unsigned char* ptr, offset_t offset) +{ + return *(uint32_t *)(ptr + offset); +} + +static inline uint64_t +ReadInt64(const unsigned char* ptr, offset_t offset) +{ + return *(uint64_t *)(ptr + offset); +} + +static inline uint16_t +ReadInt16(const void* ptr) +{ + return *(uint16_t *)(ptr); +} +static inline uint32_t +ReadInt32 (const void* ptr) +{ + return *(uint32_t *)(ptr); +} + +static inline uint64_t +ReadInt64(const void* ptr) +{ + return *(uint64_t *)(ptr); +} + +static inline uint16_t +ReadSwapInt16(const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_16(*(uint16_t *)(ptr + offset)); +} + +static inline uint32_t +ReadSwapInt32 (const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_32(*(uint32_t *)(ptr + offset)); +} +static inline uint64_t +ReadSwapInt64(const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_64(*(uint64_t *)(ptr + offset)); +} + +static inline uint16_t +ReadSwapInt16(const void* ptr) +{ + return llvm::ByteSwap_16(*(uint16_t *)(ptr)); +} + +static inline uint32_t +ReadSwapInt32 (const void* ptr) +{ + return llvm::ByteSwap_32(*(uint32_t *)(ptr)); +} +static inline uint64_t +ReadSwapInt64(const void* ptr) +{ + return llvm::ByteSwap_64(*(uint64_t *)(ptr)); +} + +#define NON_PRINTABLE_CHAR '.' +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor () : + m_start (NULL), + m_end (NULL), + m_byte_order(lldb::endian::InlHostByteOrder()), + m_addr_size (4), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// This constructor allows us to use data that is owned by someone else. +// The data must stay around as long as this object is valid. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const void* data, offset_t length, ByteOrder endian, uint32_t addr_size) : + m_start ((uint8_t*)data), + m_end ((uint8_t*)data + length), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ +} + +//---------------------------------------------------------------------- +// Make a shared pointer reference to the shared data in "data_sp" and +// set the endian swapping setting to "swap", and the address size to +// "addr_size". The shared data reference will ensure the data lives +// as long as any DataExtractor objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const DataBufferSP& data_sp, ByteOrder endian, uint32_t addr_size) : + m_start (NULL), + m_end (NULL), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ + SetData (data_sp); +} + +//---------------------------------------------------------------------- +// Initialize this object with a subset of the data bytes in "data". +// If "data" contains shared data, then a reference to this shared +// data will added and the shared data will stay around as long +// as any object contains a reference to that data. The endian +// swap and address size settings are copied from "data". +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const DataExtractor& data, offset_t offset, offset_t length) : + m_start(NULL), + m_end(NULL), + m_byte_order(data.m_byte_order), + m_addr_size(data.m_addr_size), + m_data_sp() +{ + if (data.ValidOffset(offset)) + { + offset_t bytes_available = data.GetByteSize() - offset; + if (length > bytes_available) + length = bytes_available; + SetData(data, offset, length); + } +} + +DataExtractor::DataExtractor (const DataExtractor& rhs) : + m_start (rhs.m_start), + m_end (rhs.m_end), + m_byte_order (rhs.m_byte_order), + m_addr_size (rhs.m_addr_size), + m_data_sp (rhs.m_data_sp) +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const DataExtractor& +DataExtractor::operator= (const DataExtractor& rhs) +{ + if (this != &rhs) + { + m_start = rhs.m_start; + m_end = rhs.m_end; + m_byte_order = rhs.m_byte_order; + m_addr_size = rhs.m_addr_size; + m_data_sp = rhs.m_data_sp; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DataExtractor::~DataExtractor () +{ +} + +//------------------------------------------------------------------ +// Clears the object contents back to a default invalid state, and +// release any references to shared data that this object may +// contain. +//------------------------------------------------------------------ +void +DataExtractor::Clear () +{ + m_start = NULL; + m_end = NULL; + m_byte_order = lldb::endian::InlHostByteOrder(); + m_addr_size = 4; + m_data_sp.reset(); +} + +//------------------------------------------------------------------ +// If this object contains shared data, this function returns the +// offset into that shared data. Else zero is returned. +//------------------------------------------------------------------ +size_t +DataExtractor::GetSharedDataOffset () const +{ + if (m_start != NULL) + { + const DataBuffer * data = m_data_sp.get(); + if (data != NULL) + { + const uint8_t * data_bytes = data->GetBytes(); + if (data_bytes != NULL) + { + assert(m_start >= data_bytes); + return m_start - data_bytes; + } + } + } + return 0; +} + +//---------------------------------------------------------------------- +// Set the data with which this object will extract from to data +// starting at BYTES and set the length of the data to LENGTH bytes +// long. The data is externally owned must be around at least as +// long as this object points to the data. No copy of the data is +// made, this object just refers to this data and can extract from +// it. If this object refers to any shared data upon entry, the +// reference to that data will be released. Is SWAP is set to true, +// any data extracted will be endian swapped. +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::SetData (const void *bytes, offset_t length, ByteOrder endian) +{ + m_byte_order = endian; + m_data_sp.reset(); + if (bytes == NULL || length == 0) + { + m_start = NULL; + m_end = NULL; + } + else + { + m_start = (uint8_t *)bytes; + m_end = m_start + length; + } + return GetByteSize(); +} + +//---------------------------------------------------------------------- +// Assign the data for this object to be a subrange in "data" +// starting "data_offset" bytes into "data" and ending "data_length" +// bytes later. If "data_offset" is not a valid offset into "data", +// then this object will contain no bytes. If "data_offset" is +// within "data" yet "data_length" is too large, the length will be +// capped at the number of bytes remaining in "data". If "data" +// contains a shared pointer to other data, then a ref counted +// pointer to that data will be made in this object. If "data" +// doesn't contain a shared pointer to data, then the bytes referred +// to in "data" will need to exist at least as long as this object +// refers to those bytes. The address size and endian swap settings +// are copied from the current values in "data". +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::SetData (const DataExtractor& data, offset_t data_offset, offset_t data_length) +{ + m_addr_size = data.m_addr_size; + // If "data" contains shared pointer to data, then we can use that + if (data.m_data_sp.get()) + { + m_byte_order = data.m_byte_order; + return SetData(data.m_data_sp, data.GetSharedDataOffset() + data_offset, data_length); + } + + // We have a DataExtractor object that just has a pointer to bytes + if (data.ValidOffset(data_offset)) + { + if (data_length > data.GetByteSize() - data_offset) + data_length = data.GetByteSize() - data_offset; + return SetData (data.GetDataStart() + data_offset, data_length, data.GetByteOrder()); + } + return 0; +} + +//---------------------------------------------------------------------- +// Assign the data for this object to be a subrange of the shared +// data in "data_sp" starting "data_offset" bytes into "data_sp" +// and ending "data_length" bytes later. If "data_offset" is not +// a valid offset into "data_sp", then this object will contain no +// bytes. If "data_offset" is within "data_sp" yet "data_length" is +// too large, the length will be capped at the number of bytes +// remaining in "data_sp". A ref counted pointer to the data in +// "data_sp" will be made in this object IF the number of bytes this +// object refers to in greater than zero (if at least one byte was +// available starting at "data_offset") to ensure the data stays +// around as long as it is needed. The address size and endian swap +// settings will remain unchanged from their current settings. +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::SetData (const DataBufferSP& data_sp, offset_t data_offset, offset_t data_length) +{ + m_start = m_end = NULL; + + if (data_length > 0) + { + m_data_sp = data_sp; + if (data_sp.get()) + { + const size_t data_size = data_sp->GetByteSize(); + if (data_offset < data_size) + { + m_start = data_sp->GetBytes() + data_offset; + const size_t bytes_left = data_size - data_offset; + // Cap the length of we asked for too many + if (data_length <= bytes_left) + m_end = m_start + data_length; // We got all the bytes we wanted + else + m_end = m_start + bytes_left; // Not all the bytes requested were available in the shared data + } + } + } + + size_t new_size = GetByteSize(); + + // Don't hold a shared pointer to the data buffer if we don't share + // any valid bytes in the shared buffer. + if (new_size == 0) + m_data_sp.reset(); + + return new_size; +} + +//---------------------------------------------------------------------- +// Extract a single unsigned char from the binary data and update +// the offset pointed to by "offset_ptr". +// +// RETURNS the byte that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint8_t +DataExtractor::GetU8 (offset_t *offset_ptr) const +{ + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, 1); + if (data) + return *data; + return 0; +} + +//---------------------------------------------------------------------- +// Extract "count" unsigned chars from the binary data and update the +// offset pointed to by "offset_ptr". The extracted data is copied into +// "dst". +// +// RETURNS the non-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available in +// the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU8 (offset_t *offset_ptr, void *dst, uint32_t count) const +{ + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, count); + if (data) + { + // Copy the data into the buffer + memcpy (dst, data, count); + // Return a non-NULL pointer to the converted data as an indicator of success + return dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single uint16_t from the data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the uint16_t that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint16_t +DataExtractor::GetU16 (offset_t *offset_ptr) const +{ + uint16_t val = 0; + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt16(data); + else + val = ReadInt16 (data); + } + return val; +} + +uint16_t +DataExtractor::GetU16_unchecked (offset_t *offset_ptr) const +{ + uint16_t val; + if (m_byte_order == lldb::endian::InlHostByteOrder()) + val = ReadInt16 (m_start, *offset_ptr); + else + val = ReadSwapInt16(m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +uint32_t +DataExtractor::GetU32_unchecked (offset_t *offset_ptr) const +{ + uint32_t val; + if (m_byte_order == lldb::endian::InlHostByteOrder()) + val = ReadInt32 (m_start, *offset_ptr); + else + val = ReadSwapInt32 (m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + +uint64_t +DataExtractor::GetU64_unchecked (offset_t *offset_ptr) const +{ + uint64_t val; + if (m_byte_order == lldb::endian::InlHostByteOrder()) + val = ReadInt64 (m_start, *offset_ptr); + else + val = ReadSwapInt64 (m_start, *offset_ptr); + *offset_ptr += sizeof(val); + return val; +} + + +//---------------------------------------------------------------------- +// Extract "count" uint16_t values from the binary data and update +// the offset pointed to by "offset_ptr". The extracted data is +// copied into "dst". +// +// RETURNS the non-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available +// in the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU16 (offset_t *offset_ptr, void *void_dst, uint32_t count) const +{ + const size_t src_size = sizeof(uint16_t) * count; + const uint16_t *src = (const uint16_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint16_t *dst_pos = (uint16_t *)void_dst; + uint16_t *dst_end = dst_pos + count; + const uint16_t *src_pos = src; + while (dst_pos < dst_end) + { + *dst_pos = ReadSwapInt16 (src_pos); + ++dst_pos; + ++src_pos; + } + } + else + { + memcpy (void_dst, src, src_size); + } + // Return a non-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single uint32_t from the data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the uint32_t that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::GetU32 (offset_t *offset_ptr) const +{ + uint32_t val = 0; + const uint32_t *data = (const uint32_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt32 (data); + else + val = *data; + } + return val; +} + +//---------------------------------------------------------------------- +// Extract "count" uint32_t values from the binary data and update +// the offset pointed to by "offset_ptr". The extracted data is +// copied into "dst". +// +// RETURNS the non-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available +// in the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU32 (offset_t *offset_ptr, void *void_dst, uint32_t count) const +{ + const size_t src_size = sizeof(uint32_t) * count; + const uint32_t *src = (const uint32_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint32_t *dst_pos = (uint32_t *)void_dst; + uint32_t *dst_end = dst_pos + count; + const uint32_t *src_pos = src; + while (dst_pos < dst_end) + { + *dst_pos = ReadSwapInt32 (src_pos); + ++dst_pos; + ++src_pos; + } + } + else + { + memcpy (void_dst, src, src_size); + } + // Return a non-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single uint64_t from the data and update the offset +// pointed to by "offset_ptr". +// +// RETURNS the uint64_t that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetU64 (offset_t *offset_ptr) const +{ + uint64_t val = 0; + const uint64_t *data = (const uint64_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt64 (data); + else + val = *data; + } + return val; +} + +//---------------------------------------------------------------------- +// GetU64 +// +// Get multiple consecutive 64 bit values. Return true if the entire +// read succeeds and increment the offset pointed to by offset_ptr, else +// return false and leave the offset pointed to by offset_ptr unchanged. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU64 (offset_t *offset_ptr, void *void_dst, uint32_t count) const +{ + const size_t src_size = sizeof(uint64_t) * count; + const uint64_t *src = (const uint64_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint64_t *dst_pos = (uint64_t *)void_dst; + uint64_t *dst_end = dst_pos + count; + const uint64_t *src_pos = src; + while (dst_pos < dst_end) + { + *dst_pos = ReadSwapInt64 (src_pos); + ++dst_pos; + ++src_pos; + } + } + else + { + memcpy (void_dst, src, src_size); + } + // Return a non-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extract a single integer value from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted integer +// is specified by the "byte_size" argument. "byte_size" should have +// a value between 1 and 4 since the return value is only 32 bits +// wide. Any "byte_size" values less than 1 or greater than 4 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::GetMaxU32 (offset_t *offset_ptr, size_t byte_size) const +{ + switch (byte_size) + { + case 1: return GetU8 (offset_ptr); break; + case 2: return GetU16(offset_ptr); break; + case 4: return GetU32(offset_ptr); break; + default: + assert("GetMaxU32 unhandled case!" == NULL); + break; + } + return 0; +} + +//---------------------------------------------------------------------- +// Extract a single integer value from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted integer +// is specified by the "byte_size" argument. "byte_size" should have +// a value >= 1 and <= 8 since the return value is only 64 bits +// wide. Any "byte_size" values less than 1 or greater than 8 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetMaxU64 (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return GetU8 (offset_ptr); break; + case 2: return GetU16(offset_ptr); break; + case 4: return GetU32(offset_ptr); break; + case 8: return GetU64(offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64_unchecked (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return GetU8_unchecked (offset_ptr); break; + case 2: return GetU16_unchecked (offset_ptr); break; + case 4: return GetU32_unchecked (offset_ptr); break; + case 8: return GetU64_unchecked (offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +int64_t +DataExtractor::GetMaxS64 (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return (int8_t)GetU8 (offset_ptr); break; + case 2: return (int16_t)GetU16(offset_ptr); break; + case 4: return (int32_t)GetU32(offset_ptr); break; + case 8: return (int64_t)GetU64(offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64Bitfield (offset_t *offset_ptr, size_t size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset) const +{ + uint64_t uval64 = GetMaxU64 (offset_ptr, size); + if (bitfield_bit_size > 0) + { + if (bitfield_bit_offset > 0) + uval64 >>= bitfield_bit_offset; + uint64_t bitfield_mask = ((1ul << bitfield_bit_size) - 1); + if (!bitfield_mask && bitfield_bit_offset == 0 && bitfield_bit_size == 64) + return uval64; + uval64 &= bitfield_mask; + } + return uval64; +} + +int64_t +DataExtractor::GetMaxS64Bitfield (offset_t *offset_ptr, size_t size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset) const +{ + int64_t sval64 = GetMaxS64 (offset_ptr, size); + if (bitfield_bit_size > 0) + { + if (bitfield_bit_offset > 0) + sval64 >>= bitfield_bit_offset; + uint64_t bitfield_mask = (((uint64_t)1) << bitfield_bit_size) - 1; + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & (((uint64_t)1) << (bitfield_bit_size - 1))) + sval64 |= ~bitfield_mask; + } + return sval64; +} + + +float +DataExtractor::GetFloat (offset_t *offset_ptr) const +{ + typedef float float_type; + float_type val = 0.0; + const size_t src_size = sizeof(float_type); + const float_type *src = (const float_type *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + const uint8_t *src_data = (const uint8_t *)src; + uint8_t *dst_data = (uint8_t *)&val; + for (size_t i=0; i<sizeof(float_type); ++i) + dst_data[sizeof(float_type) - 1 - i] = src_data[i]; + } + else + { + val = *src; + } + } + return val; +} + +double +DataExtractor::GetDouble (offset_t *offset_ptr) const +{ + typedef double float_type; + float_type val = 0.0; + const size_t src_size = sizeof(float_type); + const float_type *src = (const float_type *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + const uint8_t *src_data = (const uint8_t *)src; + uint8_t *dst_data = (uint8_t *)&val; + for (size_t i=0; i<sizeof(float_type); ++i) + dst_data[sizeof(float_type) - 1 - i] = src_data[i]; + } + else + { + val = *src; + } + } + return val; +} + + +long double +DataExtractor::GetLongDouble (offset_t *offset_ptr) const +{ + long double val = 0.0; +#if defined (__i386__) || defined (__amd64__) || defined (__x86_64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64) + *offset_ptr += CopyByteOrderedData (*offset_ptr, 10, &val, sizeof(val), lldb::endian::InlHostByteOrder()); +#else + *offset_ptr += CopyByteOrderedData (*offset_ptr, sizeof(val), &val, sizeof(val), lldb::endian::InlHostByteOrder()); +#endif + return val; +} + + +//------------------------------------------------------------------ +// Extract a single address from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted address +// comes from the "this->m_addr_size" member variable and should be +// set correctly prior to extracting any address values. +// +// RETURNS the address that was extracted, or zero on failure. +//------------------------------------------------------------------ +uint64_t +DataExtractor::GetAddress (offset_t *offset_ptr) const +{ + return GetMaxU64 (offset_ptr, m_addr_size); +} + +uint64_t +DataExtractor::GetAddress_unchecked (offset_t *offset_ptr) const +{ + return GetMaxU64_unchecked (offset_ptr, m_addr_size); +} + +//------------------------------------------------------------------ +// Extract a single pointer from the data and update the offset +// pointed to by "offset_ptr". The size of the extracted pointer +// comes from the "this->m_addr_size" member variable and should be +// set correctly prior to extracting any pointer values. +// +// RETURNS the pointer that was extracted, or zero on failure. +//------------------------------------------------------------------ +uint64_t +DataExtractor::GetPointer (offset_t *offset_ptr) const +{ + return GetMaxU64 (offset_ptr, m_addr_size); +} + +//---------------------------------------------------------------------- +// GetDwarfEHPtr +// +// Used for calls when the value type is specified by a DWARF EH Frame +// pointer encoding. +//---------------------------------------------------------------------- + +uint64_t +DataExtractor::GetGNUEHPointer (offset_t *offset_ptr, uint32_t eh_ptr_enc, lldb::addr_t pc_rel_addr, lldb::addr_t text_addr, lldb::addr_t data_addr)//, BSDRelocs *data_relocs) const +{ + if (eh_ptr_enc == DW_EH_PE_omit) + return ULLONG_MAX; // Value isn't in the buffer... + + uint64_t baseAddress = 0; + uint64_t addressValue = 0; + const uint32_t addr_size = GetAddressByteSize(); + + bool signExtendValue = false; + // Decode the base part or adjust our offset + switch (eh_ptr_enc & 0x70) + { + case DW_EH_PE_pcrel: + signExtendValue = true; + baseAddress = *offset_ptr; + if (pc_rel_addr != LLDB_INVALID_ADDRESS) + baseAddress += pc_rel_addr; +// else +// Log::GlobalWarning ("PC relative pointer encoding found with invalid pc relative address."); + break; + + case DW_EH_PE_textrel: + signExtendValue = true; + if (text_addr != LLDB_INVALID_ADDRESS) + baseAddress = text_addr; +// else +// Log::GlobalWarning ("text relative pointer encoding being decoded with invalid text section address, setting base address to zero."); + break; + + case DW_EH_PE_datarel: + signExtendValue = true; + if (data_addr != LLDB_INVALID_ADDRESS) + baseAddress = data_addr; +// else +// Log::GlobalWarning ("data relative pointer encoding being decoded with invalid data section address, setting base address to zero."); + break; + + case DW_EH_PE_funcrel: + signExtendValue = true; + break; + + case DW_EH_PE_aligned: + { + // SetPointerSize should be called prior to extracting these so the + // pointer size is cached + assert(addr_size != 0); + if (addr_size) + { + // Align to a address size boundary first + uint32_t alignOffset = *offset_ptr % addr_size; + if (alignOffset) + offset_ptr += addr_size - alignOffset; + } + } + break; + + default: + break; + } + + // Decode the value part + switch (eh_ptr_enc & DW_EH_PE_MASK_ENCODING) + { + case DW_EH_PE_absptr : + { + addressValue = GetAddress (offset_ptr); +// if (data_relocs) +// addressValue = data_relocs->Relocate(*offset_ptr - addr_size, *this, addressValue); + } + break; + case DW_EH_PE_uleb128 : addressValue = GetULEB128(offset_ptr); break; + case DW_EH_PE_udata2 : addressValue = GetU16(offset_ptr); break; + case DW_EH_PE_udata4 : addressValue = GetU32(offset_ptr); break; + case DW_EH_PE_udata8 : addressValue = GetU64(offset_ptr); break; + case DW_EH_PE_sleb128 : addressValue = GetSLEB128(offset_ptr); break; + case DW_EH_PE_sdata2 : addressValue = (int16_t)GetU16(offset_ptr); break; + case DW_EH_PE_sdata4 : addressValue = (int32_t)GetU32(offset_ptr); break; + case DW_EH_PE_sdata8 : addressValue = (int64_t)GetU64(offset_ptr); break; + default: + // Unhandled encoding type + assert(eh_ptr_enc); + break; + } + + // Since we promote everything to 64 bit, we may need to sign extend + if (signExtendValue && addr_size < sizeof(baseAddress)) + { + uint64_t sign_bit = 1ull << ((addr_size * 8ull) - 1ull); + if (sign_bit & addressValue) + { + uint64_t mask = ~sign_bit + 1; + addressValue |= mask; + } + } + return baseAddress + addressValue; +} + +size_t +DataExtractor::ExtractBytes (offset_t offset, offset_t length, ByteOrder dst_byte_order, void *dst) const +{ + const uint8_t *src = PeekData (offset, length); + if (src) + { + if (dst_byte_order != GetByteOrder()) + { + for (uint32_t i=0; i<length; ++i) + ((uint8_t*)dst)[i] = src[length - i - 1]; + } + else + ::memcpy (dst, src, length); + return length; + } + return 0; +} + +// Extract data and swap if needed when doing the copy +lldb::offset_t +DataExtractor::CopyByteOrderedData (offset_t src_offset, + offset_t src_len, + void *dst_void_ptr, + offset_t dst_len, + ByteOrder dst_byte_order) const +{ + // Validate the source info + if (!ValidOffsetForDataOfSize(src_offset, src_len)) + assert (ValidOffsetForDataOfSize(src_offset, src_len)); + assert (src_len > 0); + assert (m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle); + + // Validate the destination info + assert (dst_void_ptr != NULL); + assert (dst_len > 0); + assert (dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle); + + // Must have valid byte orders set in this object and for destination + if (!(dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle) || + !(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle)) + return 0; + + uint32_t i; + uint8_t* dst = (uint8_t*)dst_void_ptr; + const uint8_t* src = (const uint8_t *)PeekData (src_offset, src_len); + if (src) + { + if (dst_len >= src_len) + { + // We are copying the entire value from src into dst. + // Calculate how many, if any, zeroes we need for the most + // significant bytes if "dst_len" is greater than "src_len"... + const size_t num_zeroes = dst_len - src_len; + if (dst_byte_order == eByteOrderBig) + { + // Big endian, so we lead with zeroes... + if (num_zeroes > 0) + ::memset (dst, 0, num_zeroes); + // Then either copy or swap the rest + if (m_byte_order == eByteOrderBig) + { + ::memcpy (dst + num_zeroes, src, src_len); + } + else + { + for (i=0; i<src_len; ++i) + dst[i+num_zeroes] = src[src_len - 1 - i]; + } + } + else + { + // Little endian destination, so we lead the value bytes + if (m_byte_order == eByteOrderBig) + { + for (i=0; i<src_len; ++i) + dst[i] = src[src_len - 1 - i]; + } + else + { + ::memcpy (dst, src, src_len); + } + // And zero the rest... + if (num_zeroes > 0) + ::memset (dst + src_len, 0, num_zeroes); + } + return src_len; + } + else + { + // We are only copying some of the value from src into dst.. + + if (dst_byte_order == eByteOrderBig) + { + // Big endian dst + if (m_byte_order == eByteOrderBig) + { + // Big endian dst, with big endian src + ::memcpy (dst, src + (src_len - dst_len), dst_len); + } + else + { + // Big endian dst, with little endian src + for (i=0; i<dst_len; ++i) + dst[i] = src[dst_len - 1 - i]; + } + } + else + { + // Little endian dst + if (m_byte_order == eByteOrderBig) + { + // Little endian dst, with big endian src + for (i=0; i<dst_len; ++i) + dst[i] = src[src_len - 1 - i]; + } + else + { + // Little endian dst, with big endian src + ::memcpy (dst, src, dst_len); + } + } + return dst_len; + } + + } + return 0; +} + + +//---------------------------------------------------------------------- +// Extracts a variable length NULL terminated C string from +// the data at the offset pointed to by "offset_ptr". The +// "offset_ptr" will be updated with the offset of the byte that +// follows the NULL terminator byte. +// +// If the offset pointed to by "offset_ptr" is out of bounds, or if +// "length" is non-zero and there aren't enough avaialable +// bytes, NULL will be returned and "offset_ptr" will not be +// updated. +//---------------------------------------------------------------------- +const char* +DataExtractor::GetCStr (offset_t *offset_ptr) const +{ + const char *cstr = (const char *)PeekData (*offset_ptr, 1); + if (cstr) + { + const char *cstr_end = cstr; + const char *end = (const char *)m_end; + while (cstr_end < end && *cstr_end) + ++cstr_end; + + // Now we are either at the end of the data or we point to the + // NULL C string terminator with cstr_end... + if (*cstr_end == '\0') + { + // Advance the offset with one extra byte for the NULL terminator + *offset_ptr += (cstr_end - cstr + 1); + return cstr; + } + + // We reached the end of the data without finding a NULL C string + // terminator. Fall through and return NULL otherwise anyone that + // would have used the result as a C string can wonder into + // unknown memory... + } + return NULL; +} + +//---------------------------------------------------------------------- +// Extracts a NULL terminated C string from the fixed length field of +// length "len" at the offset pointed to by "offset_ptr". +// The "offset_ptr" will be updated with the offset of the byte that +// follows the fixed length field. +// +// If the offset pointed to by "offset_ptr" is out of bounds, or if +// the offset plus the length of the field is out of bounds, or if the +// field does not contain a NULL terminator byte, NULL will be returned +// and "offset_ptr" will not be updated. +//---------------------------------------------------------------------- +const char* +DataExtractor::GetCStr (offset_t *offset_ptr, offset_t len) const +{ + const char *cstr = (const char *)PeekData (*offset_ptr, len); + if (cstr) + { + if (memchr (cstr, '\0', len) == NULL) + { + return NULL; + } + *offset_ptr += len; + return cstr; + } + return NULL; +} + +//------------------------------------------------------------------ +// Peeks at a string in the contained data. No verification is done +// to make sure the entire string lies within the bounds of this +// object's data, only "offset" is verified to be a valid offset. +// +// Returns a valid C string pointer if "offset" is a valid offset in +// this object's data, else NULL is returned. +//------------------------------------------------------------------ +const char * +DataExtractor::PeekCStr (offset_t offset) const +{ + return (const char *)PeekData (offset, 1); +} + +//---------------------------------------------------------------------- +// Extracts an unsigned LEB128 number from this object's data +// starting at the offset pointed to by "offset_ptr". The offset +// pointed to by "offset_ptr" will be updated with the offset of the +// byte following the last extracted byte. +// +// Returned the extracted integer value. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetULEB128 (offset_t *offset_ptr) const +{ + const uint8_t *src = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + return 0; + + const uint8_t *end = m_end; + + if (src < end) + { + uint64_t result = *src++; + if (result >= 0x80) + { + result &= 0x7f; + int shift = 7; + while (src < end) + { + uint8_t byte = *src++; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + } + *offset_ptr = src - m_start; + return result; + } + + return 0; +} + +//---------------------------------------------------------------------- +// Extracts an signed LEB128 number from this object's data +// starting at the offset pointed to by "offset_ptr". The offset +// pointed to by "offset_ptr" will be updated with the offset of the +// byte following the last extracted byte. +// +// Returned the extracted integer value. +//---------------------------------------------------------------------- +int64_t +DataExtractor::GetSLEB128 (offset_t *offset_ptr) const +{ + const uint8_t *src = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + return 0; + + const uint8_t *end = m_end; + + if (src < end) + { + int64_t result = 0; + int shift = 0; + int size = sizeof (int64_t) * 8; + + uint8_t byte = 0; + int bytecount = 0; + + while (src < end) + { + bytecount++; + byte = *src++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + + // Sign bit of byte is 2nd high order bit (0x40) + if (shift < size && (byte & 0x40)) + result |= - (1 << shift); + + *offset_ptr += bytecount; + return result; + } + return 0; +} + +//---------------------------------------------------------------------- +// Skips a ULEB128 number (signed or unsigned) from this object's +// data starting at the offset pointed to by "offset_ptr". The +// offset pointed to by "offset_ptr" will be updated with the offset +// of the byte following the last extracted byte. +// +// Returns the number of bytes consumed during the extraction. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::Skip_LEB128 (offset_t *offset_ptr) const +{ + uint32_t bytes_consumed = 0; + const uint8_t *src = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + return 0; + + const uint8_t *end = m_end; + + if (src < end) + { + const uint8_t *src_pos = src; + while ((src_pos < end) && (*src_pos++ & 0x80)) + ++bytes_consumed; + *offset_ptr += src_pos - src; + } + return bytes_consumed; +} + +static bool +GetAPInt (const DataExtractor &data, lldb::offset_t *offset_ptr, lldb::offset_t byte_size, llvm::APInt &result) +{ + 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); + } + result = llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array)); + return true; + } + 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; + result = llvm::APInt(byte_size * 8, llvm::ArrayRef<uint64_t>(uint64_array)); + return true; + } + return false; +} + +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::APInt apint; + if (GetAPInt (data, &offset, byte_size, apint)) + { + std::string apint_str(apint.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; +} + +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 * 0x1.0p125f; + } + + v <<= 13; + u.u = v | 0x70000000U; + return u.f * 0x1.0p-112f; +} + +lldb::offset_t +DataExtractor::Dump (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) const +{ + if (s == NULL) + 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(), NULL, NULL)); + 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, *this, 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); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disassembler_sp->GetInstructionList().Clear(); + } + } + } + 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; 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), ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, LLDB_INVALID_OFFSET, 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))); + line_start_offset = offset; + } + else + if (item_format != eFormatChar && + item_format != eFormatCharPrintable && + item_format != eFormatCharArray && + count > 0) + { + s->PutChar(' '); + } + + uint32_t i; + switch (item_format) + { + case eFormatBoolean: + if (item_byte_size <= 8) + s->Printf ("%s", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset) ? "true" : "false"); + else + { + s->Printf("error: unsupported byte size (%zu) for boolean format", item_byte_size); + return offset; + } + break; + + case eFormatBinary: + if (item_byte_size <= 8) + { + uint64_t uval64 = 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 (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, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatBytes: + case eFormatBytesWithASCII: + for (i=0; i<item_byte_size; ++i) + { + s->Printf ("%2.2x", 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: + { + // 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 = 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, GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = true; + const unsigned radix = 10; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatUnsigned: + if (item_byte_size <= 8) + s->Printf ("%" PRIu64, GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = false; + const unsigned radix = 10; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatOctal: + if (item_byte_size <= 8) + s->Printf ("0%" PRIo64, GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = false; + const unsigned radix = 8; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatOSType: + { + uint64_t uval64 = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + s->PutChar('\''); + for (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 = 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(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 <= 8) + { + s->Printf("%" PRIu64, GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); + s->Printf(" + %" PRIu64 "i", GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); + } + else + { + s->Printf("error: unsupported byte size (%zu) for complex integer format", item_byte_size); + return offset; + } + } + break; + + case eFormatComplex: + if (sizeof(float) * 2 == item_byte_size) + { + float f32_1 = GetFloat (&offset); + float f32_2 = GetFloat (&offset); + + s->Printf ("%g + %gi", f32_1, f32_2); + break; + } + else if (sizeof(double) * 2 == item_byte_size) + { + double d64_1 = GetDouble (&offset); + double d64_2 = GetDouble (&offset); + + s->Printf ("%lg + %lgi", d64_1, d64_2); + break; + } + else if (sizeof(long double) * 2 == item_byte_size) + { + long double ld64_1 = GetLongDouble (&offset); + long double ld64_2 = GetLongDouble (&offset); + s->Printf ("%Lg + %Lgi", ld64_1, ld64_2); + break; + } + else + { + s->Printf("error: unsupported byte size (%zu) for complex float format", item_byte_size); + return offset; + } + break; + + default: + case eFormatDefault: + case eFormatHex: + case eFormatHexUppercase: + { + bool wantsuppercase = (item_format == eFormatHexUppercase); + if (item_byte_size <= 8) + { + s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64, (int)(2 * item_byte_size), (int)(2 * item_byte_size), GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + } + else + { + assert (item_bit_size == 0 && item_bit_offset == 0); + s->PutCString("0x"); + const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); + if (bytes) + { + uint32_t idx; + if (m_byte_order == 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; + + case eFormatFloat: + { + TargetSP target_sp; + bool used_apfloat = false; + if (exe_scope) + target_sp = exe_scope->CalculateTarget(); + if (target_sp) + { + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + { + clang::ASTContext *ast = clang_ast->getASTContext(); + if (ast) + { + llvm::SmallVector<char, 256> sv; + // Show full precision when printing float values + const unsigned format_precision = 0; + const unsigned format_max_padding = 100; + size_t item_bit_size = item_byte_size * 8; + + if (item_bit_size == ast->getTypeSize(ast->FloatTy)) + { + llvm::APInt apint(item_bit_size, this->GetMaxU64(&offset, item_byte_size)); + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->FloatTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + else if (item_bit_size == ast->getTypeSize(ast->DoubleTy)) + { + llvm::APInt apint; + if (GetAPInt (*this, &offset, item_byte_size, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->DoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + } + else if (item_bit_size == ast->getTypeSize(ast->LongDoubleTy)) + { + llvm::APInt apint; + switch (target_sp->GetArchitecture().GetCore()) + { + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_64_x86_64: + // clang will assert when contructing the apfloat if we use a 16 byte integer value + if (GetAPInt (*this, &offset, 10, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->LongDoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + break; + + default: + if (GetAPInt (*this, &offset, item_byte_size, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->LongDoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + break; + } + } + else if (item_bit_size == ast->getTypeSize(ast->HalfTy)) + { + llvm::APInt apint(item_bit_size, this->GetU16(&offset)); + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->HalfTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + + if (!sv.empty()) + { + s->Printf("%*.*s", (int)sv.size(), (int)sv.size(), sv.data()); + used_apfloat = true; + } + } + } + } + + if (!used_apfloat) + { + std::ostringstream ss; + if (item_byte_size == sizeof(float) || item_byte_size == 2) + { + float f; + if (item_byte_size == 2) + { + uint16_t half = this->GetU16(&offset); + f = half2float(half); + } + else + { + f = 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 << GetDouble(&offset); + } + else if (item_byte_size == sizeof(long double) || item_byte_size == 10) + { + ss.precision(std::numeric_limits<long double>::digits10); + ss << GetLongDouble(&offset); + } + else + { + s->Printf("error: unsupported byte size (%zu) for float format", item_byte_size); + return offset; + } + ss.flush(); + s->Printf("%s", ss.str().c_str()); + } + } + break; + + case eFormatUnicode16: + s->Printf("U+%4.4x", GetU16 (&offset)); + break; + + case eFormatUnicode32: + s->Printf("U+0x%8.8x", GetU32 (&offset)); + break; + + case eFormatAddressInfo: + { + addr_t addr = 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 (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 (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 (%zu) for hex float format", 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 = Dump (s, offset, eFormatCharArray, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt8: + s->PutChar('{'); + offset = Dump (s, offset, eFormatDecimal, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt8: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt16: + s->PutChar('{'); + offset = Dump (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 = Dump (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 = Dump (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 = Dump (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 = Dump (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 = Dump (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 eFormatVectorOfFloat32: + s->PutChar('{'); + offset = Dump (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 = Dump (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 = Dump (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), ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, LLDB_INVALID_OFFSET, LLDB_INVALID_ADDRESS, 0, 0); + } + return offset; // Return the offset at which we ended up +} + +//---------------------------------------------------------------------- +// Dumps bytes from this object's data to the stream "s" starting +// "start_offset" bytes into this data, and ending with the byte +// before "end_offset". "base_addr" will be added to the offset +// into the dumped data when showing the offset into the data in the +// output information. "num_per_line" objects of type "type" will +// be dumped with the option to override the format for each object +// with "type_format". "type_format" is a printf style formatting +// string. If "type_format" is NULL, then an appropriate format +// string will be used for the supplied "type". If the stream "s" +// is NULL, then the output will be send to Log(). +//---------------------------------------------------------------------- +lldb::offset_t +DataExtractor::PutToLog +( + Log *log, + offset_t start_offset, + offset_t length, + uint64_t base_addr, + uint32_t num_per_line, + DataExtractor::Type type, + const char *format +) const +{ + if (log == NULL) + return start_offset; + + offset_t offset; + offset_t end_offset; + uint32_t count; + StreamString sstr; + for (offset = start_offset, end_offset = offset + length, count = 0; ValidOffset(offset) && offset < end_offset; ++count) + { + if ((count % num_per_line) == 0) + { + // Print out any previous string + if (sstr.GetSize() > 0) + { + log->Printf("%s", sstr.GetData()); + sstr.Clear(); + } + // Reset string offset and fill the current line string with address: + if (base_addr != LLDB_INVALID_ADDRESS) + sstr.Printf("0x%8.8" PRIx64 ":", (uint64_t)(base_addr + (offset - start_offset))); + } + + switch (type) + { + case TypeUInt8: sstr.Printf (format ? format : " %2.2x", GetU8(&offset)); break; + case TypeChar: + { + char ch = GetU8(&offset); + sstr.Printf (format ? format : " %c", isprint(ch) ? ch : ' '); + } + break; + case TypeUInt16: sstr.Printf (format ? format : " %4.4x", GetU16(&offset)); break; + case TypeUInt32: sstr.Printf (format ? format : " %8.8x", GetU32(&offset)); break; + case TypeUInt64: sstr.Printf (format ? format : " %16.16" PRIx64, GetU64(&offset)); break; + case TypePointer: sstr.Printf (format ? format : " 0x%" PRIx64, GetAddress(&offset)); break; + case TypeULEB128: sstr.Printf (format ? format : " 0x%" PRIx64, GetULEB128(&offset)); break; + case TypeSLEB128: sstr.Printf (format ? format : " %" PRId64, GetSLEB128(&offset)); break; + } + } + + if (sstr.GetSize() > 0) + log->Printf("%s", sstr.GetData()); + + return offset; // Return the offset at which we ended up +} + +//---------------------------------------------------------------------- +// DumpUUID +// +// Dump out a UUID starting at 'offset' bytes into the buffer +//---------------------------------------------------------------------- +void +DataExtractor::DumpUUID (Stream *s, offset_t offset) const +{ + if (s) + { + const uint8_t *uuid_data = PeekData(offset, 16); + if ( uuid_data ) + { + lldb_private::UUID uuid(uuid_data, 16); + uuid.Dump(s); + } + else + { + s->Printf("<not enough data for UUID at offset 0x%8.8" PRIx64 ">", offset); + } + } +} + +void +DataExtractor::DumpHexBytes (Stream *s, + const void *src, + size_t src_len, + uint32_t bytes_per_line, + addr_t base_addr) +{ + DataExtractor data (src, src_len, eByteOrderLittle, 4); + data.Dump (s, + 0, // Offset into "src" + 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 +} + +size_t +DataExtractor::Copy (DataExtractor &dest_data) const +{ + if (m_data_sp.get()) + { + // we can pass along the SP to the data + dest_data.SetData(m_data_sp); + } + else + { + const uint8_t *base_ptr = m_start; + size_t data_size = GetByteSize(); + dest_data.SetData(DataBufferSP(new DataBufferHeap(base_ptr, data_size))); + } + return GetByteSize(); +} + +bool +DataExtractor::Append(DataExtractor& rhs) +{ + if (rhs.GetByteOrder() != GetByteOrder()) + return false; + + if (rhs.GetByteSize() == 0) + return true; + + if (GetByteSize() == 0) + return (rhs.Copy(*this) > 0); + + size_t bytes = GetByteSize() + rhs.GetByteSize(); + + DataBufferHeap *buffer_heap_ptr = NULL; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (buffer_sp.get() == NULL || buffer_heap_ptr == NULL) + return false; + + uint8_t* bytes_ptr = buffer_heap_ptr->GetBytes(); + + memcpy(bytes_ptr, GetDataStart(), GetByteSize()); + memcpy(bytes_ptr + GetByteSize(), rhs.GetDataStart(), rhs.GetByteSize()); + + SetData(buffer_sp); + + return true; +} + +bool +DataExtractor::Append(void* buf, offset_t length) +{ + if (buf == NULL) + return false; + + if (length == 0) + return true; + + size_t bytes = GetByteSize() + length; + + DataBufferHeap *buffer_heap_ptr = NULL; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (buffer_sp.get() == NULL || buffer_heap_ptr == NULL) + return false; + + uint8_t* bytes_ptr = buffer_heap_ptr->GetBytes(); + + if (GetByteSize() > 0) + memcpy(bytes_ptr, GetDataStart(), GetByteSize()); + + memcpy(bytes_ptr + GetByteSize(), buf, length); + + SetData(buffer_sp); + + return true; +} diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp new file mode 100644 index 0000000..d1d2ebb --- /dev/null +++ b/source/Core/Debugger.cpp @@ -0,0 +1,2695 @@ +//===-- Debugger.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" + +#include "lldb/Core/Debugger.h" + +#include <map> + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamAsynchronousIO.h" +#include "lldb/Core/StreamCallback.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/DynamicLibrary.h" +#include "lldb/Host/Terminal.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/AnsiTerminal.h" + +using namespace lldb; +using namespace lldb_private; + + +static uint32_t g_shared_debugger_refcount = 0; +static lldb::user_id_t g_unique_id = 1; + +#pragma mark Static Functions + +static Mutex & +GetDebuggerListMutex () +{ + static Mutex g_mutex(Mutex::eMutexTypeRecursive); + return g_mutex; +} + +typedef std::vector<DebuggerSP> DebuggerList; + +static DebuggerList & +GetDebuggerList() +{ + // hide the static debugger list inside a singleton accessor to avoid + // global init contructors + static DebuggerList g_list; + return g_list; +} + +OptionEnumValueElement +g_show_disassembly_enum_values[] = +{ + { Debugger::eStopDisassemblyTypeNever, "never", "Never show disassembly when displaying a stop context."}, + { 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."}, + { 0, NULL, NULL } +}; + +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."}, + { 0, NULL, NULL } +}; + +#define MODULE_WITH_FUNC "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" +#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}" + +#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id%tid}"\ + "{, ${frame.pc}}"\ + MODULE_WITH_FUNC\ + FILE_AND_LINE\ + "{, name = '${thread.name}'}"\ + "{, queue = '${thread.queue}'}"\ + "{, stop reason = ${thread.stop-reason}}"\ + "{\\nReturn value: ${thread.return-value}}"\ + "\\n" + +#define DEFAULT_FRAME_FORMAT "frame #${frame.index}: ${frame.pc}"\ + MODULE_WITH_FUNC\ + FILE_AND_LINE\ + "\\n" + + + +static PropertyDefinition +g_properties[] = +{ +{ "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, +{ "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, +{ "notify-void", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, +{ "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, +{ "script-lang", OptionValue::eTypeEnum , true, eScriptLanguagePython, NULL, g_language_enumerators, "The script language to be used for evaluating user-written scripts." }, +{ "stop-disassembly-count", OptionValue::eTypeSInt64 , true, 4 , NULL, NULL, "The number of disassembly lines to show when displaying a stopped context." }, +{ "stop-disassembly-display", OptionValue::eTypeEnum , true, Debugger::eStopDisassemblyTypeNoSource, NULL, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." }, +{ "stop-line-count-after", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come after the current source line when displaying a stopped context." }, +{ "stop-line-count-before", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "The number of sources lines to display that come before the current source line when displaying a stopped context." }, +{ "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL, NULL, "The maximum number of columns to use for displaying text." }, +{ "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, +{ "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, +{ "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, + + { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } +}; + +enum +{ + ePropertyAutoConfirm = 0, + ePropertyFrameFormat, + ePropertyNotiftVoid, + ePropertyPrompt, + ePropertyScriptLanguage, + ePropertyStopDisassemblyCount, + ePropertyStopDisassemblyDisplay, + ePropertyStopLineCountAfter, + ePropertyStopLineCountBefore, + ePropertyTerminalWidth, + ePropertyThreadFormat, + ePropertyUseExternalEditor, + ePropertyUseColor, +}; + +// +//const char * +//Debugger::GetFrameFormat() const +//{ +// return m_properties_sp->GetFrameFormat(); +//} +//const char * +//Debugger::GetThreadFormat() const +//{ +// return m_properties_sp->GetThreadFormat(); +//} +// + + +Error +Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value) +{ + bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0; + 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(); + } + Error 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 (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0) + { + const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); + EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); + GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); + } + else if (strcmp(property_path, g_properties[ePropertyUseColor].name) == 0) + { + // 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<Error> errors; + StreamString feedback_stream; + if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) + { + for (auto error : errors) + { + GetErrorStream().Printf("%s\n",error.AsCString()); + } + if (feedback_stream.GetSize()) + GetErrorStream().Printf("%s",feedback_stream.GetData()); + } + } + } + } + return error; +} + +bool +Debugger::GetAutoConfirm () const +{ + const uint32_t idx = ePropertyAutoConfirm; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +const char * +Debugger::GetFrameFormat() const +{ + const uint32_t idx = ePropertyFrameFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +bool +Debugger::GetNotifyVoid () const +{ + const uint32_t idx = ePropertyNotiftVoid; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +const char * +Debugger::GetPrompt() const +{ + const uint32_t idx = ePropertyPrompt; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +void +Debugger::SetPrompt(const char *p) +{ + const uint32_t idx = ePropertyPrompt; + m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p); + const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); + EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));; + GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); +} + +const char * +Debugger::GetThreadFormat() const +{ + const uint32_t idx = ePropertyThreadFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +lldb::ScriptLanguage +Debugger::GetScriptLanguage() const +{ + const uint32_t idx = ePropertyScriptLanguage; + return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); +} + +bool +Debugger::SetScriptLanguage (lldb::ScriptLanguage script_lang) +{ + const uint32_t idx = ePropertyScriptLanguage; + return m_collection_sp->SetPropertyAtIndexAsEnumeration (NULL, idx, script_lang); +} + +uint32_t +Debugger::GetTerminalWidth () const +{ + const uint32_t idx = ePropertyTerminalWidth; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +bool +Debugger::SetTerminalWidth (uint32_t term_width) +{ + const uint32_t idx = ePropertyTerminalWidth; + return m_collection_sp->SetPropertyAtIndexAsSInt64 (NULL, idx, term_width); +} + +bool +Debugger::GetUseExternalEditor () const +{ + const uint32_t idx = ePropertyUseExternalEditor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseExternalEditor (bool b) +{ + const uint32_t idx = ePropertyUseExternalEditor; + return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); +} + +bool +Debugger::GetUseColor () const +{ + const uint32_t idx = ePropertyUseColor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseColor (bool b) +{ + const uint32_t idx = ePropertyUseColor; + bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); + SetPrompt (GetPrompt()); + return ret; +} + +uint32_t +Debugger::GetStopSourceLineCount (bool before) const +{ + const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +Debugger::StopDisassemblyType +Debugger::GetStopDisassemblyDisplay () const +{ + const uint32_t idx = ePropertyStopDisassemblyDisplay; + return (Debugger::StopDisassemblyType)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); +} + +uint32_t +Debugger::GetDisassemblyLineCount () const +{ + const uint32_t idx = ePropertyStopDisassemblyCount; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +#pragma mark Debugger + +//const DebuggerPropertiesSP & +//Debugger::GetSettings() const +//{ +// return m_properties_sp; +//} +// + +int +Debugger::TestDebuggerRefCount () +{ + return g_shared_debugger_refcount; +} + +void +Debugger::Initialize () +{ + if (g_shared_debugger_refcount++ == 0) + lldb_private::Initialize(); +} + +void +Debugger::Terminate () +{ + if (g_shared_debugger_refcount > 0) + { + g_shared_debugger_refcount--; + if (g_shared_debugger_refcount == 0) + { + lldb_private::WillTerminate(); + lldb_private::Terminate(); + + // Clear our master list of debugger objects + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().clear(); + } + } +} + +void +Debugger::SettingsInitialize () +{ + Target::SettingsInitialize (); +} + +void +Debugger::SettingsTerminate () +{ + Target::SettingsTerminate (); +} + +bool +Debugger::LoadPlugin (const FileSpec& spec, Error& error) +{ + lldb::DynamicLibrarySP dynlib_sp(new lldb_private::DynamicLibrary(spec)); + if (!dynlib_sp || dynlib_sp->IsValid() == false) + { + if (spec.Exists()) + error.SetErrorString("this file does not represent a loadable dylib"); + else + error.SetErrorString("no such file"); + return false; + } + lldb::DebuggerSP debugger_sp(shared_from_this()); + lldb::SBDebugger debugger_sb(debugger_sp); + // This calls the bool lldb::PluginInitialize(lldb::SBDebugger debugger) function. + // TODO: mangle this differently for your system - on OSX, the first underscore needs to be removed and the second one stays + LLDBCommandPluginInit init_func = dynlib_sp->GetSymbol<LLDBCommandPluginInit>("_ZN4lldb16PluginInitializeENS_10SBDebuggerE"); + if (!init_func) + { + error.SetErrorString("cannot find the initialization function lldb::PluginInitialize(lldb::SBDebugger)"); + return false; + } + if (init_func(debugger_sb)) + { + m_loaded_plugins.push_back(dynlib_sp); + return true; + } + error.SetErrorString("dylib refused to be loaded"); + return false; +} + +static FileSpec::EnumerateDirectoryResult +LoadPluginCallback +( + void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec + ) +{ + Error error; + + static ConstString g_dylibext("dylib"); + static ConstString g_solibext("so"); + + if (!baton) + return FileSpec::eEnumerateDirectoryResultQuit; + + Debugger *debugger = (Debugger*)baton; + + // 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 (file_type == FileSpec::eFileTypeRegular || + file_type == FileSpec::eFileTypeSymbolicLink || + file_type == FileSpec::eFileTypeUnknown ) + { + FileSpec plugin_file_spec (file_spec); + plugin_file_spec.ResolvePath (); + + if (plugin_file_spec.GetFileNameExtension() != g_dylibext && + plugin_file_spec.GetFileNameExtension() != g_solibext) + { + return FileSpec::eEnumerateDirectoryResultNext; + } + + Error plugin_load_error; + debugger->LoadPlugin (plugin_file_spec, plugin_load_error); + + return FileSpec::eEnumerateDirectoryResultNext; + } + + else if (file_type == FileSpec::eFileTypeUnknown || + file_type == FileSpec::eFileTypeDirectory || + file_type == FileSpec::eFileTypeSymbolicLink ) + { + // 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 enurating a file system that doesn't have correct file type + // information. + return FileSpec::eEnumerateDirectoryResultEnter; + } + + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +Debugger::InstanceInitialize () +{ + FileSpec dir_spec; + const bool find_directories = true; + const bool find_files = true; + const bool find_other = true; + char dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypeLLDBSystemPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + this); + } + } + + if (Host::GetLLDBPath (ePathTypeLLDBUserPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::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_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().push_back(debugger_sp); + } + debugger_sp->InstanceInitialize (); + return debugger_sp; +} + +void +Debugger::Destroy (DebuggerSP &debugger_sp) +{ + if (debugger_sp.get() == NULL) + return; + + debugger_sp->Clear(); + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList (); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin (); pos != end; ++pos) + { + if ((*pos).get() == debugger_sp.get()) + { + debugger_list.erase (pos); + return; + } + } + } +} + +DebuggerSP +Debugger::FindDebuggerWithInstanceName (const ConstString &instance_name) +{ + DebuggerSP debugger_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + + for (pos = debugger_list.begin(); pos != end; ++pos) + { + if ((*pos).get()->m_instance_name == instance_name) + { + debugger_sp = *pos; + break; + } + } + } + return debugger_sp; +} + +TargetSP +Debugger::FindTargetWithProcessID (lldb::pid_t pid) +{ + TargetSP target_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.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_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.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(OptionValuePropertiesSP(new OptionValueProperties())), + m_input_comm("debugger.input"), + m_input_file (), + m_output_file (), + m_error_file (), + m_terminal_state (), + m_target_list (*this), + m_platform_list (), + m_listener ("lldb.Debugger"), + m_source_manager_ap(), + m_source_file_cache(), + m_command_interpreter_ap (new CommandInterpreter (*this, eScriptLanguageDefault, false)), + m_input_reader_stack (), + m_input_reader_data (), + m_instance_name() +{ + 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.reset (new StreamCallback (log_callback, baton)); + m_command_interpreter_ap->Initialize (); + // Always add our default platform to the platform list + PlatformSP default_platform_sp (Platform::GetDefaultPlatform()); + assert (default_platform_sp.get()); + m_platform_list.Append (default_platform_sp, true); + + m_collection_sp->Initialize (g_properties); + m_collection_sp->AppendProperty (ConstString("target"), + ConstString("Settings specify to debugging targets."), + true, + Target::GetGlobalProperties()->GetValueProperties()); + if (m_command_interpreter_ap.get()) + { + m_collection_sp->AppendProperty (ConstString("interpreter"), + ConstString("Settings specify to the debugger's command interpreter."), + true, + m_command_interpreter_ap->GetValueProperties()); + } + OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64 (NULL, 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); +} + +Debugger::~Debugger () +{ + Clear(); +} + +void +Debugger::Clear() +{ + CleanUpInputReaders(); + m_listener.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(); + } + } + BroadcasterManager::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 (); + // Now that we have closed m_input_file, we can now tell our input communication + // class to close down. Its read thread should quickly exit after we close + // the input file handle above. + m_input_comm.Clear (); +} + +bool +Debugger::GetCloseInputOnEOF () const +{ + return m_input_comm.GetCloseOnEOF(); +} + +void +Debugger::SetCloseInputOnEOF (bool b) +{ + m_input_comm.SetCloseOnEOF(b); +} + +bool +Debugger::GetAsyncExecution () +{ + return !m_command_interpreter_ap->GetSynchronous(); +} + +void +Debugger::SetAsyncExecution (bool async_execution) +{ + m_command_interpreter_ap->SetSynchronous (!async_execution); +} + + +void +Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &in_file = GetInputFile(); + in_file.SetStream (fh, tranfer_ownership); + if (in_file.IsValid() == false) + in_file.SetStream (stdin, true); + + // Disconnect from any old connection if we had one + m_input_comm.Disconnect (); + // Pass false as the second argument to ConnectionFileDescriptor below because + // our "in_file" above will already take ownership if requested and we don't + // want to objects trying to own and close a file descriptor. + m_input_comm.SetConnection (new ConnectionFileDescriptor (in_file.GetDescriptor(), false)); + m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); + + // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState. + SaveInputTerminalState (); + + Error error; + if (m_input_comm.StartReadThread (&error) == false) + { + File &err_file = GetErrorFile(); + + err_file.Printf ("error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); + exit(1); + } +} + +void +Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &out_file = GetOutputFile(); + out_file.SetStream (fh, tranfer_ownership); + if (out_file.IsValid() == false) + out_file.SetStream (stdout, false); + + // do not create the ScriptInterpreter just for setting the output file handle + // as the constructor will know how to do the right thing on its own + const bool can_create = false; + ScriptInterpreter* script_interpreter = GetCommandInterpreter().GetScriptInterpreter(can_create); + if (script_interpreter) + script_interpreter->ResetOutputFileHandle (fh); +} + +void +Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &err_file = GetErrorFile(); + err_file.SetStream (fh, tranfer_ownership); + if (err_file.IsValid() == false) + err_file.SetStream (stderr, false); +} + +void +Debugger::SaveInputTerminalState () +{ + File &in_file = GetInputFile(); + if (in_file.GetDescriptor() != File::kInvalidDescriptor) + m_terminal_state.Save(in_file.GetDescriptor(), 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() == false) + { + 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() == NULL) + exe_ctx.SetFrameSP (thread_sp->GetStackFrameAtIndex (0)); + } + } + } + return exe_ctx; + +} + +InputReaderSP +Debugger::GetCurrentInputReader () +{ + InputReaderSP reader_sp; + + if (!m_input_reader_stack.IsEmpty()) + { + // Clear any finished readers from the stack + while (CheckIfTopInputReaderIsDone()) ; + + if (!m_input_reader_stack.IsEmpty()) + reader_sp = m_input_reader_stack.Top(); + } + + return reader_sp; +} + +void +Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) +{ + if (bytes_len > 0) + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); + else + ((Debugger *)baton)->DispatchInputEndOfFile (); +} + + +void +Debugger::DispatchInput (const char *bytes, size_t bytes_len) +{ + if (bytes == NULL || bytes_len == 0) + return; + + WriteToDefaultReader (bytes, bytes_len); +} + +void +Debugger::DispatchInputInterrupt () +{ + m_input_reader_data.clear(); + + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderInterrupt); + + // If notifying the reader of the interrupt finished the reader, we should pop it off the stack. + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::DispatchInputEndOfFile () +{ + m_input_reader_data.clear(); + + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderEndOfFile); + + // If notifying the reader of the end-of-file finished the reader, we should pop it off the stack. + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::CleanUpInputReaders () +{ + m_input_reader_data.clear(); + + // The bottom input reader should be the main debugger input reader. We do not want to close that one here. + while (m_input_reader_stack.GetSize() > 1) + { + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderEndOfFile); + reader_sp->SetIsDone (true); + } + } +} + +void +Debugger::NotifyTopInputReader (InputReaderAction notification) +{ + InputReaderSP reader_sp (GetCurrentInputReader()); + if (reader_sp) + { + reader_sp->Notify (notification); + + // Flush out any input readers that are done. + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + } +} + +bool +Debugger::InputReaderIsTopReader (const InputReaderSP& reader_sp) +{ + InputReaderSP top_reader_sp (GetCurrentInputReader()); + + return (reader_sp.get() == top_reader_sp.get()); +} + + +void +Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) +{ + if (bytes && bytes_len) + m_input_reader_data.append (bytes, bytes_len); + + if (m_input_reader_data.empty()) + return; + + while (!m_input_reader_stack.IsEmpty() && !m_input_reader_data.empty()) + { + // Get the input reader from the top of the stack + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (!reader_sp) + break; + + size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.c_str(), + m_input_reader_data.size()); + if (bytes_handled) + { + m_input_reader_data.erase (0, bytes_handled); + } + else + { + // No bytes were handled, we might not have reached our + // granularity, just return and wait for more data + break; + } + } + + // Flush out any input readers that are done. + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + +} + +void +Debugger::PushInputReader (const InputReaderSP& reader_sp) +{ + if (!reader_sp) + return; + + // Deactivate the old top reader + InputReaderSP top_reader_sp (GetCurrentInputReader ()); + + if (top_reader_sp) + top_reader_sp->Notify (eInputReaderDeactivate); + + m_input_reader_stack.Push (reader_sp); + reader_sp->Notify (eInputReaderActivate); + ActivateInputReader (reader_sp); +} + +bool +Debugger::PopInputReader (const InputReaderSP& pop_reader_sp) +{ + bool result = false; + + // The reader on the stop of the stack is done, so let the next + // read on the stack referesh its prompt and if there is one... + if (!m_input_reader_stack.IsEmpty()) + { + // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. + InputReaderSP reader_sp(m_input_reader_stack.Top()); + + if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) + { + m_input_reader_stack.Pop (); + reader_sp->Notify (eInputReaderDeactivate); + reader_sp->Notify (eInputReaderDone); + result = true; + + if (!m_input_reader_stack.IsEmpty()) + { + reader_sp = m_input_reader_stack.Top(); + if (reader_sp) + { + ActivateInputReader (reader_sp); + reader_sp->Notify (eInputReaderReactivate); + } + } + } + } + return result; +} + +bool +Debugger::CheckIfTopInputReaderIsDone () +{ + bool result = false; + if (!m_input_reader_stack.IsEmpty()) + { + // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. + InputReaderSP reader_sp(m_input_reader_stack.Top()); + + if (reader_sp && reader_sp->IsDone()) + { + result = true; + PopInputReader (reader_sp); + } + } + return result; +} + +void +Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +{ + int input_fd = m_input_file.GetFile().GetDescriptor(); + + if (input_fd >= 0) + { + Terminal tty(input_fd); + + tty.SetEcho(reader_sp->GetEcho()); + + switch (reader_sp->GetGranularity()) + { + case eInputReaderGranularityByte: + case eInputReaderGranularityWord: + tty.SetCanonical (false); + break; + + case eInputReaderGranularityLine: + case eInputReaderGranularityAll: + tty.SetCanonical (true); + break; + + default: + break; + } + } +} + +StreamSP +Debugger::GetAsyncOutputStream () +{ + return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), + CommandInterpreter::eBroadcastBitAsynchronousOutputData)); +} + +StreamSP +Debugger::GetAsyncErrorStream () +{ + return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), + CommandInterpreter::eBroadcastBitAsynchronousErrorData)); +} + +size_t +Debugger::GetNumDebuggers() +{ + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + return GetDebuggerList().size(); + } + return 0; +} + +lldb::DebuggerSP +Debugger::GetDebuggerAtIndex (size_t index) +{ + DebuggerSP debugger_sp; + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + + if (index < debugger_list.size()) + debugger_sp = debugger_list[index]; + } + + return debugger_sp; +} + +DebuggerSP +Debugger::FindDebuggerWithID (lldb::user_id_t id) +{ + DebuggerSP debugger_sp; + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin(); pos != end; ++pos) + { + if ((*pos).get()->GetID() == id) + { + debugger_sp = *pos; + break; + } + } + } + return debugger_sp; +} + +static void +TestPromptFormats (StackFrame *frame) +{ + if (frame == NULL) + return; + + StreamString s; + const char *prompt_format = + "{addr = '${addr}'\n}" + "{process.id = '${process.id}'\n}" + "{process.name = '${process.name}'\n}" + "{process.file.basename = '${process.file.basename}'\n}" + "{process.file.fullpath = '${process.file.fullpath}'\n}" + "{thread.id = '${thread.id}'\n}" + "{thread.index = '${thread.index}'\n}" + "{thread.name = '${thread.name}'\n}" + "{thread.queue = '${thread.queue}'\n}" + "{thread.stop-reason = '${thread.stop-reason}'\n}" + "{target.arch = '${target.arch}'\n}" + "{module.file.basename = '${module.file.basename}'\n}" + "{module.file.fullpath = '${module.file.fullpath}'\n}" + "{file.basename = '${file.basename}'\n}" + "{file.fullpath = '${file.fullpath}'\n}" + "{frame.index = '${frame.index}'\n}" + "{frame.pc = '${frame.pc}'\n}" + "{frame.sp = '${frame.sp}'\n}" + "{frame.fp = '${frame.fp}'\n}" + "{frame.flags = '${frame.flags}'\n}" + "{frame.reg.rdi = '${frame.reg.rdi}'\n}" + "{frame.reg.rip = '${frame.reg.rip}'\n}" + "{frame.reg.rsp = '${frame.reg.rsp}'\n}" + "{frame.reg.rbp = '${frame.reg.rbp}'\n}" + "{frame.reg.rflags = '${frame.reg.rflags}'\n}" + "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" + "{frame.reg.carp = '${frame.reg.carp}'\n}" + "{function.id = '${function.id}'\n}" + "{function.name = '${function.name}'\n}" + "{function.name-with-args = '${function.name-with-args}'\n}" + "{function.addr-offset = '${function.addr-offset}'\n}" + "{function.line-offset = '${function.line-offset}'\n}" + "{function.pc-offset = '${function.pc-offset}'\n}" + "{line.file.basename = '${line.file.basename}'\n}" + "{line.file.fullpath = '${line.file.fullpath}'\n}" + "{line.number = '${line.number}'\n}" + "{line.start-addr = '${line.start-addr}'\n}" + "{line.end-addr = '${line.end-addr}'\n}" +; + + SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything)); + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s)) + { + printf("%s\n", s.GetData()); + } + else + { + printf ("what we got: %s\n", s.GetData()); + } +} + +static bool +ScanFormatDescriptor (const char* var_name_begin, + const char* var_name_end, + const char** var_name_final, + const char** percent_position, + Format* custom_format, + ValueObject::ValueObjectRepresentationStyle* val_obj_display) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + *percent_position = ::strchr(var_name_begin,'%'); + if (!*percent_position || *percent_position > var_name_end) + { + if (log) + log->Printf("[ScanFormatDescriptor] no format descriptor in string, skipping"); + *var_name_final = var_name_end; + } + else + { + *var_name_final = *percent_position; + std::string format_name(*var_name_final+1, var_name_end-*var_name_final-1); + if (log) + log->Printf("[ScanFormatDescriptor] parsing %s as a format descriptor", format_name.c_str()); + if ( !FormatManager::GetFormatFromCString(format_name.c_str(), + true, + *custom_format) ) + { + if (log) + log->Printf("[ScanFormatDescriptor] %s is an unknown format", format_name.c_str()); + + switch (format_name.front()) + { + case '@': // if this is an @ sign, print ObjC description + *val_obj_display = ValueObject::eValueObjectRepresentationStyleLanguageSpecific; + break; + case 'V': // if this is a V, print the value using the default format + *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + break; + case 'L': // if this is an L, print the location of the value + *val_obj_display = ValueObject::eValueObjectRepresentationStyleLocation; + break; + case 'S': // if this is an S, print the summary after all + *val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; + break; + case '#': // if this is a '#', print the number of children + *val_obj_display = ValueObject::eValueObjectRepresentationStyleChildrenCount; + break; + case 'T': // if this is a 'T', print the type + *val_obj_display = ValueObject::eValueObjectRepresentationStyleType; + break; + case 'N': // if this is a 'N', print the name + *val_obj_display = ValueObject::eValueObjectRepresentationStyleName; + break; + case '>': // if this is a '>', print the name + *val_obj_display = ValueObject::eValueObjectRepresentationStyleExpressionPath; + break; + default: + if (log) + log->Printf("ScanFormatDescriptor] %s is an error, leaving the previous value alone", format_name.c_str()); + break; + } + } + // a good custom format tells us to print the value using it + else + { + if (log) + log->Printf("[ScanFormatDescriptor] will display value for this VO"); + *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + } + if (log) + log->Printf("[ScanFormatDescriptor] final format description outcome: custom_format = %d, val_obj_display = %d", + *custom_format, + *val_obj_display); + return true; +} + +static bool +ScanBracketedRange (const char* var_name_begin, + const char* var_name_end, + const char* var_name_final, + const char** open_bracket_position, + const char** separator_position, + const char** close_bracket_position, + const char** var_name_final_if_array_range, + int64_t* index_lower, + int64_t* index_higher) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + *open_bracket_position = ::strchr(var_name_begin,'['); + if (*open_bracket_position && *open_bracket_position < var_name_final) + { + *separator_position = ::strchr(*open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield + *close_bracket_position = ::strchr(*open_bracket_position,']'); + // as usual, we assume that [] will come before % + //printf("trying to expand a []\n"); + *var_name_final_if_array_range = *open_bracket_position; + if (*close_bracket_position - *open_bracket_position == 1) + { + if (log) + log->Printf("[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); + *index_lower = 0; + } + else if (*separator_position == NULL || *separator_position > var_name_end) + { + char *end = NULL; + *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); + *index_higher = *index_lower; + if (log) + log->Printf("[ScanBracketedRange] [%" PRId64 "] detected, high index is same", *index_lower); + } + else if (*close_bracket_position && *close_bracket_position < var_name_end) + { + char *end = NULL; + *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); + *index_higher = ::strtoul (*separator_position+1, &end, 0); + if (log) + log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected", *index_lower, *index_higher); + } + else + { + if (log) + log->Printf("[ScanBracketedRange] expression is erroneous, cannot extract indices out of it"); + return false; + } + if (*index_lower > *index_higher && *index_higher > 0) + { + if (log) + log->Printf("[ScanBracketedRange] swapping indices"); + int64_t temp = *index_lower; + *index_lower = *index_higher; + *index_higher = temp; + } + } + else if (log) + log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); + return true; +} + +template <typename T> +static bool RunScriptFormatKeyword(Stream &s, ScriptInterpreter *script_interpreter, T t, const std::string& script_name) +{ + if (script_interpreter) + { + Error script_error; + std::string script_output; + + if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), t, script_output, script_error) && script_error.Success()) + { + s.Printf("%s", script_output.c_str()); + return true; + } + else + { + s.Printf("<error: %s>",script_error.AsCString()); + } + } + return false; +} + +static ValueObjectSP +ExpandIndexedExpression (ValueObject* valobj, + size_t index, + StackFrame* frame, + bool deref_pointer) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + const char* ptr_deref_format = "[%d]"; + std::string ptr_deref_buffer(10,0); + ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index); + if (log) + log->Printf("[ExpandIndexedExpression] name to deref: %s",ptr_deref_buffer.c_str()); + const char* first_unparsed; + 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(), + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &what_next); + if (!item) + { + if (log) + log->Printf("[ExpandIndexedExpression] ERROR: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + else + { + if (log) + log->Printf("[ExpandIndexedExpression] ALL RIGHT: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + return item; +} + +static inline bool +IsToken(const char *var_name_begin, const char *var) +{ + return (::strncmp (var_name_begin, var, strlen(var)) == 0); +} + +static bool +IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &format, const char *default_format, + const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) +{ + int var_len = strlen(var); + if (::strncmp (var_name_begin, var, var_len) == 0) + { + var_name_begin += var_len; + if (*var_name_begin == '}') + { + format = default_format; + return true; + } + else if (*var_name_begin == '%') + { + // Allow format specifiers: x|X|u with optional width specifiers. + // ${thread.id%x} ; hex + // ${thread.id%X} ; uppercase hex + // ${thread.id%u} ; unsigned decimal + // ${thread.id%8.8X} ; width.precision + specifier + // ${thread.id%tid} ; unsigned on FreeBSD/Linux, otherwise default_format (0x%4.4x for thread.id) + int dot_count = 0; + const char *specifier = NULL; + int width_precision_length = 0; + const char *width_precision = ++var_name_begin; + while (isdigit(*var_name_begin) || *var_name_begin == '.') + { + dot_count += (*var_name_begin == '.'); + if (dot_count > 1) + break; + var_name_begin++; + width_precision_length++; + } + + if (IsToken (var_name_begin, "tid}")) + { + Target *target = Target::GetTargetFromContexts (exe_ctx_ptr, sc_ptr); + if (target) + { + 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)) + specifier = PRIu64; + } + if (!specifier) + { + format = default_format; + return true; + } + } + else if (IsToken (var_name_begin, "x}")) + specifier = PRIx64; + else if (IsToken (var_name_begin, "X}")) + specifier = PRIX64; + else if (IsToken (var_name_begin, "u}")) + specifier = PRIu64; + + if (specifier) + { + format = "%"; + if (width_precision_length) + format += std::string(width_precision, width_precision_length); + format += specifier; + return true; + } + } + } + return false; +} + +static bool +FormatPromptRecurse +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + const char **end, + ValueObject* valobj +) +{ + ValueObject* realvalobj = NULL; // makes it super-easy to parse pointers + bool success = true; + const char *p; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (p = format; *p != '\0'; ++p) + { + if (realvalobj) + { + valobj = realvalobj; + realvalobj = NULL; + } + size_t non_special_chars = ::strcspn (p, "${}\\"); + if (non_special_chars > 0) + { + if (success) + s.Write (p, non_special_chars); + p += non_special_chars; + } + + if (*p == '\0') + { + break; + } + else if (*p == '{') + { + // Start a new scope that must have everything it needs if it is to + // to make it into the final output stream "s". If you want to make + // a format that only prints out the function or symbol name if there + // is one in the symbol context you can use: + // "{function =${function.name}}" + // The first '{' starts a new scope that end with the matching '}' at + // the end of the string. The contents "function =${function.name}" + // will then be evaluated and only be output if there is a function + // or symbol with a valid name. + StreamString sub_strm; + + ++p; // Skip the '{' + + if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) + { + // The stream had all it needed + s.Write(sub_strm.GetData(), sub_strm.GetSize()); + } + if (*p != '}') + { + success = false; + break; + } + } + else if (*p == '}') + { + // End of a enclosing scope + break; + } + else if (*p == '$') + { + // We have a prompt variable to print + ++p; + if (*p == '{') + { + ++p; + const char *var_name_begin = p; + const char *var_name_end = ::strchr (p, '}'); + + if (var_name_end && var_name_begin < var_name_end) + { + // if we have already failed to parse, skip this variable + if (success) + { + const char *cstr = NULL; + std::string token_format; + Address format_addr; + bool calculate_format_addr_function_offset = false; + // Set reg_kind and reg_num to invalid values + RegisterKind reg_kind = kNumRegisterKinds; + uint32_t reg_num = LLDB_INVALID_REGNUM; + FileSpec format_file_spec; + const RegisterInfo *reg_info = NULL; + RegisterContext *reg_ctx = NULL; + bool do_deref_pointer = false; + ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; + + // Each variable must set success to true below... + bool var_success = false; + switch (var_name_begin[0]) + { + case '*': + case 'v': + case 's': + { + if (!valobj) + break; + + if (log) + log->Printf("[Debugger::FormatPrompt] initial string: %s",var_name_begin); + + // check for *var and *svar + if (*var_name_begin == '*') + { + do_deref_pointer = true; + var_name_begin++; + if (log) + log->Printf("[Debugger::FormatPrompt] found a deref, new string is: %s",var_name_begin); + } + + if (*var_name_begin == 's') + { + if (!valobj->IsSynthetic()) + valobj = valobj->GetSyntheticValue().get(); + if (!valobj) + break; + var_name_begin++; + if (log) + log->Printf("[Debugger::FormatPrompt] found a synthetic, new string is: %s",var_name_begin); + } + + // should be a 'v' by now + if (*var_name_begin != 'v') + break; + + if (log) + log->Printf("[Debugger::FormatPrompt] string I am working with: %s",var_name_begin); + + ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? + ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); + ValueObject::GetValueForExpressionPathOptions options; + options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().DoAllowSyntheticChildren(); + ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; + ValueObject* target = NULL; + Format custom_format = eFormatInvalid; + const char* var_name_final = NULL; + const char* var_name_final_if_array_range = NULL; + const char* close_bracket_position = NULL; + int64_t index_lower = -1; + int64_t index_higher = -1; + bool is_array_range = false; + const char* first_unparsed; + bool was_plain_var = false; + bool was_var_format = false; + bool was_var_indexed = false; + + if (!valobj) break; + // simplest case ${var}, just print valobj's value + if (IsToken (var_name_begin, "var}")) + { + was_plain_var = true; + target = valobj; + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + else if (IsToken (var_name_begin,"var%")) + { + was_var_format = true; + // this is a variable with some custom format applied to it + const char* percent_position; + target = valobj; + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + ScanFormatDescriptor (var_name_begin, + var_name_end, + &var_name_final, + &percent_position, + &custom_format, + &val_obj_display); + } + // this is ${var.something} or multiple .something nested + else if (IsToken (var_name_begin, "var")) + { + if (IsToken (var_name_begin, "var[")) + was_var_indexed = true; + const char* percent_position; + ScanFormatDescriptor (var_name_begin, + var_name_end, + &var_name_final, + &percent_position, + &custom_format, + &val_obj_display); + + const char* open_bracket_position; + const char* separator_position; + ScanBracketedRange (var_name_begin, + var_name_end, + var_name_final, + &open_bracket_position, + &separator_position, + &close_bracket_position, + &var_name_final_if_array_range, + &index_lower, + &index_higher); + + Error error; + + std::string expr_path(var_name_final-var_name_begin-1,0); + memcpy(&expr_path[0], var_name_begin+3,var_name_final-var_name_begin-3); + + if (log) + log->Printf("[Debugger::FormatPrompt] symbol to expand: %s",expr_path.c_str()); + + target = valobj->GetValueForExpressionPath(expr_path.c_str(), + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &what_next).get(); + + if (!target) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + break; + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] ALL RIGHT: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + } + else + break; + + 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 + Error error; + target = target->Dereference(error).get(); + if (error.Fail()) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", error.AsCString("unknown")); \ + break; + } + do_deref_pointer = false; + } + + // <rdar://problem/11338654> + // 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()); + lldb::TypeNameSpecifierImplSP type_sp(new TypeNameSpecifierImpl(bitfield_name.GetData(),false)); + if (!DataVisualization::GetSummaryForType(type_sp)) + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + + // TODO use flags for these + const uint32_t type_info_flags = target->GetClangType().GetTypeInfo(NULL); + bool is_array = (type_info_flags & ClangASTType::eTypeIsArray) != 0; + bool is_pointer = (type_info_flags & ClangASTType::eTypeIsPointer) != 0; + bool is_aggregate = target->GetClangType().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; + if (log) + log->Printf("[Debugger::FormatPrompt] I am into array || pointer && !range"); + + if (target->HasSpecialPrintableRepresentation(val_obj_display, custom_format)) + { + // try to use the special cases + var_success = target->DumpPrintableRepresentation(str_temp, + val_obj_display, + custom_format); + if (log) + log->Printf("[Debugger::FormatPrompt] special cases did%s match", var_success ? "" : "n't"); + + // should not happen + if (var_success) + s << str_temp.GetData(); + var_success = true; + break; + } + 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::ePrintableRepresentationSpecialCasesDisable); + } + var_success = true; + break; + } + } + + // 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(); + var_success = true; + break; + } + + // 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>"; + var_success = true; + break; + } + + if (!is_array_range) + { + if (log) + log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); + var_success = target->DumpPrintableRepresentation(s,val_obj_display, custom_format); + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] checking if I can handle as array"); + if (!is_array && !is_pointer) + break; + if (log) + log->Printf("[Debugger::FormatPrompt] handle as array"); + const char* special_directions = NULL; + StreamString special_directions_writer; + if (close_bracket_position && (var_name_end-close_bracket_position > 1)) + { + ConstString additional_data; + additional_data.SetCStringWithLength(close_bracket_position+1, var_name_end-close_bracket_position-1); + special_directions_writer.Printf("${%svar%s}", + do_deref_pointer ? "*" : "", + additional_data.GetCString()); + special_directions = special_directions_writer.GetData(); + } + + // let us display items index_lower thru index_higher of this array + s.PutChar('['); + var_success = true; + + if (index_higher < 0) + index_higher = valobj->GetNumChildren() - 1; + + uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + for (;index_lower<=index_higher;index_lower++) + { + ValueObject* item = ExpandIndexedExpression (target, + index_lower, + exe_ctx->GetFramePtr(), + false).get(); + + if (!item) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at index %" PRId64, index_lower); + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] special_directions for child item: %s",special_directions); + } + + if (!special_directions) + var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); + else + var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item); + + if (--max_num_children == 0) + { + s.PutCString(", ..."); + break; + } + + if (index_lower < index_higher) + s.PutChar(','); + } + s.PutChar(']'); + } + } + break; + case 'a': + if (IsToken (var_name_begin, "addr}")) + { + if (addr && addr->IsValid()) + { + var_success = true; + format_addr = *addr; + } + } + break; + + case 'p': + if (IsToken (var_name_begin, "process.")) + { + if (exe_ctx) + { + Process *process = exe_ctx->GetProcessPtr(); + if (process) + { + var_name_begin += ::strlen ("process."); + if (IsTokenWithFormat (var_name_begin, "id", token_format, "%" PRIu64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), process->GetID()); + var_success = true; + } + else if ((IsToken (var_name_begin, "name}")) || + (IsToken (var_name_begin, "file.basename}")) || + (IsToken (var_name_begin, "file.fullpath}"))) + { + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + if (var_name_begin[0] == 'n' || var_name_begin[5] == 'f') + { + format_file_spec.GetFilename() = exe_module->GetFileSpec().GetFilename(); + var_success = format_file_spec; + } + else + { + format_file_spec = exe_module->GetFileSpec(); + var_success = format_file_spec; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = process->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, process, script_name)) + var_success = true; + } + } + } + } + break; + + case 't': + if (IsToken (var_name_begin, "thread.")) + { + if (exe_ctx) + { + Thread *thread = exe_ctx->GetThreadPtr(); + if (thread) + { + var_name_begin += ::strlen ("thread."); + if (IsTokenWithFormat (var_name_begin, "id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), thread->GetID()); + var_success = true; + } + else if (IsTokenWithFormat (var_name_begin, "protocol_id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), thread->GetProtocolID()); + var_success = true; + } + else if (IsTokenWithFormat (var_name_begin, "index", token_format, "%" PRIu64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), (uint64_t)thread->GetIndexID()); + var_success = true; + } + else if (IsToken (var_name_begin, "name}")) + { + cstr = thread->GetName(); + var_success = cstr && cstr[0]; + if (var_success) + s.PutCString(cstr); + } + else if (IsToken (var_name_begin, "queue}")) + { + cstr = thread->GetQueueName(); + var_success = cstr && cstr[0]; + if (var_success) + s.PutCString(cstr); + } + else if (IsToken (var_name_begin, "stop-reason}")) + { + StopInfoSP stop_info_sp = thread->GetStopInfo (); + if (stop_info_sp && stop_info_sp->IsValid()) + { + cstr = stop_info_sp->GetDescription(); + if (cstr && cstr[0]) + { + s.PutCString(cstr); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "return-value}")) + { + 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) + { + ValueObject::DumpValueObject (s, return_valobj_sp.get()); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = thread->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name)) + var_success = true; + } + } + } + } + else if (IsToken (var_name_begin, "target.")) + { + // TODO: hookup properties +// if (!target_properties_sp) +// { +// Target *target = Target::GetTargetFromContexts (exe_ctx, sc); +// if (target) +// target_properties_sp = target->GetProperties(); +// } +// +// if (target_properties_sp) +// { +// var_name_begin += ::strlen ("target."); +// const char *end_property = strchr(var_name_begin, '}'); +// if (end_property) +// { +// ConstString property_name(var_name_begin, end_property - var_name_begin); +// std::string property_value (target_properties_sp->GetPropertyValue(property_name)); +// if (!property_value.empty()) +// { +// s.PutCString (property_value.c_str()); +// var_success = true; +// } +// } +// } + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + if (target) + { + var_name_begin += ::strlen ("target."); + if (IsToken (var_name_begin, "arch}")) + { + ArchSpec arch (target->GetArchitecture ()); + if (arch.IsValid()) + { + s.PutCString (arch.GetArchitectureName()); + var_success = true; + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, target, script_name)) + var_success = true; + } + } + } + break; + + + case 'm': + if (IsToken (var_name_begin, "module.")) + { + if (sc && sc->module_sp.get()) + { + Module *module = sc->module_sp.get(); + var_name_begin += ::strlen ("module."); + + if (IsToken (var_name_begin, "file.")) + { + if (module->GetFileSpec()) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = module->GetFileSpec().GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = module->GetFileSpec(); + var_success = format_file_spec; + } + } + } + } + } + break; + + + case 'f': + if (IsToken (var_name_begin, "file.")) + { + if (sc && sc->comp_unit != NULL) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = sc->comp_unit->GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = *sc->comp_unit; + var_success = format_file_spec; + } + } + } + else if (IsToken (var_name_begin, "frame.")) + { + if (exe_ctx) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) + { + var_name_begin += ::strlen ("frame."); + if (IsToken (var_name_begin, "index}")) + { + s.Printf("%u", frame->GetFrameIndex()); + var_success = true; + } + else if (IsToken (var_name_begin, "pc}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_PC; + var_success = true; + } + else if (IsToken (var_name_begin, "sp}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + var_success = true; + } + else if (IsToken (var_name_begin, "fp}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FP; + var_success = true; + } + else if (IsToken (var_name_begin, "flags}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FLAGS; + var_success = true; + } + else if (IsToken (var_name_begin, "reg.")) + { + reg_ctx = frame->GetRegisterContext().get(); + if (reg_ctx) + { + var_name_begin += ::strlen ("reg."); + if (var_name_begin < var_name_end) + { + std::string reg_name (var_name_begin, var_name_end); + reg_info = reg_ctx->GetRegisterInfoByName (reg_name.c_str()); + if (reg_info) + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = frame->GetThread()->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, frame, script_name)) + var_success = true; + } + } + } + } + else if (IsToken (var_name_begin, "function.")) + { + if (sc && (sc->function != NULL || sc->symbol != NULL)) + { + var_name_begin += ::strlen ("function."); + if (IsToken (var_name_begin, "id}")) + { + if (sc->function) + s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID()); + else + s.Printf("symbol[%u]", sc->symbol->GetID()); + + var_success = true; + } + else if (IsToken (var_name_begin, "name}")) + { + if (sc->function) + cstr = sc->function->GetName().AsCString (NULL); + else if (sc->symbol) + cstr = sc->symbol->GetName().AsCString (NULL); + if (cstr) + { + s.PutCString(cstr); + + 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().Dump(&s); + } + } + } + var_success = true; + } + } + else if (IsToken (var_name_begin, "name-with-args}")) + { + // Print the function name with arguments in it + + if (sc->function) + { + var_success = true; + ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL; + cstr = sc->function->GetName().AsCString (NULL); + if (cstr) + { + const InlineFunctionInfo *inline_info = NULL; + 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().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 = NULL; + 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) + { + VariableSP var_sp (args.GetVariableAtIndex (arg_idx)); + ValueObjectSP var_value_sp (ValueObjectVariable::Create (exe_scope, var_sp)); + const char *var_name = var_value_sp->GetName().GetCString(); + const char *var_value = var_value_sp->GetValueAsCString(); + if (arg_idx > 0) + s.PutCString (", "); + if (var_value_sp->GetError().Success()) + { + if (var_value) + s.Printf ("%s=%s", var_name, var_value); + 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); + } + } + } + else if (sc->symbol) + { + cstr = sc->symbol->GetName().AsCString (NULL); + if (cstr) + { + s.PutCString(cstr); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "addr-offset}")) + { + var_success = addr != NULL; + if (var_success) + { + format_addr = *addr; + calculate_format_addr_function_offset = true; + } + } + else if (IsToken (var_name_begin, "line-offset}")) + { + var_success = sc->line_entry.range.GetBaseAddress().IsValid(); + if (var_success) + { + format_addr = sc->line_entry.range.GetBaseAddress(); + calculate_format_addr_function_offset = true; + } + } + else if (IsToken (var_name_begin, "pc-offset}")) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + var_success = frame != NULL; + if (var_success) + { + format_addr = frame->GetFrameCodeAddress(); + calculate_format_addr_function_offset = true; + } + } + } + } + break; + + case 'l': + if (IsToken (var_name_begin, "line.")) + { + if (sc && sc->line_entry.IsValid()) + { + var_name_begin += ::strlen ("line."); + if (IsToken (var_name_begin, "file.")) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = sc->line_entry.file.GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = sc->line_entry.file; + var_success = format_file_spec; + } + } + else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64, exe_ctx, sc)) + { + var_success = true; + s.Printf(token_format.c_str(), (uint64_t)sc->line_entry.line); + } + else if ((IsToken (var_name_begin, "start-addr}")) || + (IsToken (var_name_begin, "end-addr}"))) + { + var_success = sc && sc->line_entry.range.GetBaseAddress().IsValid(); + if (var_success) + { + format_addr = sc->line_entry.range.GetBaseAddress(); + if (var_name_begin[0] == 'e') + format_addr.Slide (sc->line_entry.range.GetByteSize()); + } + } + } + } + break; + } + + if (var_success) + { + // If format addr is valid, then we need to print an address + if (reg_num != LLDB_INVALID_REGNUM) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + // We have a register value to display... + if (reg_num == LLDB_REGNUM_GENERIC_PC && reg_kind == eRegisterKindGeneric) + { + format_addr = frame->GetFrameCodeAddress(); + } + else + { + if (reg_ctx == NULL) + reg_ctx = frame->GetRegisterContext().get(); + + if (reg_ctx) + { + if (reg_kind != kNumRegisterKinds) + reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); + var_success = reg_info != NULL; + } + } + } + + if (reg_info != NULL) + { + RegisterValue reg_value; + var_success = reg_ctx->ReadRegister (reg_info, reg_value); + if (var_success) + { + reg_value.Dump(&s, reg_info, false, false, eFormatDefault); + } + } + + if (format_file_spec) + { + s << format_file_spec; + } + + // If format addr is valid, then we need to print an address + if (format_addr.IsValid()) + { + var_success = false; + + if (calculate_format_addr_function_offset) + { + Address func_addr; + + if (sc) + { + if (sc->function) + { + func_addr = sc->function->GetAddressRange().GetBaseAddress(); + if (sc->block) + { + // 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->GetAddress(); + } + + if (func_addr.IsValid()) + { + 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) + s.Printf(" + %" PRIu64, addr_file_addr - func_file_addr); + else if (addr_file_addr < func_file_addr) + s.Printf(" - %" PRIu64, func_file_addr - addr_file_addr); + var_success = 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) + s.Printf(" + %" PRIu64, addr_load_addr - func_load_addr); + else if (addr_load_addr < func_load_addr) + s.Printf(" - %" PRIu64, func_load_addr - addr_load_addr); + var_success = true; + } + } + } + } + else + { + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + addr_t vaddr = LLDB_INVALID_ADDRESS; + if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) + vaddr = format_addr.GetLoadAddress (target); + if (vaddr == LLDB_INVALID_ADDRESS) + vaddr = format_addr.GetFileAddress (); + + if (vaddr != LLDB_INVALID_ADDRESS) + { + int addr_width = target->GetArchitecture().GetAddressByteSize() * 2; + if (addr_width == 0) + addr_width = 16; + s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + var_success = true; + } + } + } + } + + if (var_success == false) + success = false; + } + p = var_name_end; + } + else + break; + } + else + { + // We got a dollar sign with no '{' after it, it must just be a dollar sign + s.PutChar(*p); + } + } + else if (*p == '\\') + { + ++p; // skip the slash + switch (*p) + { + case 'a': s.PutChar ('\a'); break; + case 'b': s.PutChar ('\b'); break; + case 'f': s.PutChar ('\f'); break; + case 'n': s.PutChar ('\n'); break; + case 'r': s.PutChar ('\r'); break; + case 't': s.PutChar ('\t'); break; + case 'v': s.PutChar ('\v'); break; + case '\'': s.PutChar ('\''); break; + case '\\': s.PutChar ('\\'); 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; (p[i] >= '0' && p[i] <= '7') && i<4; ++i) + oct_str[i] = p[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) + p += i - 1; + unsigned long octal_value = ::strtoul (oct_str, NULL, 8); + if (octal_value <= UINT8_MAX) + { + s.PutChar((char)octal_value); + } + } + break; + + case 'x': + // hex number in the format + if (isxdigit(p[1])) + { + ++p; // Skip the 'x' + + // Make a string that can hold onto two hex chars plus a + // NULL terminator + char hex_str[3] = { 0,0,0 }; + hex_str[0] = *p; + if (isxdigit(p[1])) + { + ++p; // Skip the first of the two hex chars + hex_str[1] = *p; + } + + unsigned long hex_value = strtoul (hex_str, NULL, 16); + if (hex_value <= UINT8_MAX) + s.PutChar ((char)hex_value); + } + else + { + s.PutChar('x'); + } + break; + + default: + // Just desensitize any other character by just printing what + // came after the '\' + s << *p; + break; + + } + + } + } + if (end) + *end = p; + return success; +} + +bool +Debugger::FormatPrompt +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + ValueObject* valobj +) +{ + bool use_color = exe_ctx ? exe_ctx->GetTargetRef().GetDebugger().GetUseColor() : true; + std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); + if (format_str.length()) + format = format_str.c_str(); + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj); +} + +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.reset (new StreamCallback (log_callback, baton)); +} + +bool +Debugger::EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream) +{ + Log::Callbacks log_callbacks; + + StreamSP 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 == NULL || *log_file == '\0') + { + log_stream_sp.reset(new StreamFile(GetOutputFile().GetDescriptor(), false)); + } + else + { + LogStreamMap::iterator pos = m_log_streams.find(log_file); + if (pos != m_log_streams.end()) + log_stream_sp = pos->second.lock(); + if (!log_stream_sp) + { + log_stream_sp.reset (new StreamFile (log_file)); + m_log_streams[log_file] = log_stream_sp; + } + } + assert (log_stream_sp.get()); + + if (log_options == 0) + log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; + + if (Log::GetLogChannelCallbacks (ConstString(channel), log_callbacks)) + { + log_callbacks.enable (log_stream_sp, log_options, categories, &error_stream); + return true; + } + else + { + LogChannelSP log_channel_sp (LogChannel::FindPlugin (channel)); + if (log_channel_sp) + { + if (log_channel_sp->Enable (log_stream_sp, log_options, &error_stream, categories)) + { + return true; + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + return false; +} + +SourceManager & +Debugger::GetSourceManager () +{ + if (m_source_manager_ap.get() == NULL) + m_source_manager_ap.reset (new SourceManager (shared_from_this())); + return *m_source_manager_ap; +} + + diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp new file mode 100644 index 0000000..e80e92c --- /dev/null +++ b/source/Core/Disassembler.cpp @@ -0,0 +1,1269 @@ +//===-- Disassembler.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/Disassembler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.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) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "Disassembler::FindPlugin (arch = %s, plugin_name = %s)", + arch.GetArchitectureName(), + plugin_name); + + DisassemblerCreateInstance create_callback = NULL; + + 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.get()) + return disassembler_sp; + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + DisassemblerSP disassembler_sp(create_callback(arch, flavor)); + + if (disassembler_sp.get()) + 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 == NULL) + { + // 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) + { + if (target->GetSectionLoadList().IsEmpty()) + { + target->GetImages().ResolveFileAddress (addr.GetOffset(), resolved_addr); + } + else + { + target->GetSectionLoadList().ResolveLoadAddress (addr.GetOffset(), resolved_addr); + } + // We weren't able to resolve the address, just treat it as a + // raw address + if (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, + 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) == false) + 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, + 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, + const ConstString &name, + Module *module, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + SymbolContextList sc_list; + if (name) + { + const bool include_symbols = true; + const bool include_inlines = true; + if (module) + { + module->FindFunctions (name, + NULL, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + true, + sc_list); + } + else if (exe_ctx.GetTargetPtr()) + { + exe_ctx.GetTargetPtr()->GetImages().FindFunctions (name, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + false, + sc_list); + } + } + + if (sc_list.GetSize ()) + { + return Disassemble (debugger, + arch, + plugin_name, + flavor, + exe_ctx, + sc_list, + num_instructions, + num_mixed_context_lines, + options, + strm); + } + return false; +} + + +lldb::DisassemblerSP +Disassembler::DisassembleRange +( + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &range +) +{ + lldb::DisassemblerSP disasm_sp; + if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid()) + { + disasm_sp = Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name); + + if (disasm_sp) + { + const bool prefer_file_cache = false; + size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, prefer_file_cache); + if (bytes_disassembled == 0) + disasm_sp.reset(); + } + } + 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) +{ + lldb::DisassemblerSP disasm_sp; + + if (src) + { + disasm_sp = Disassembler::FindPlugin(arch, flavor, plugin_name); + + if (disasm_sp) + { + 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, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + if (disasm_range.GetByteSize()) + { + lldb::DisassemblerSP disasm_sp (Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), arch, flavor, plugin_name)); + + if (disasm_sp.get()) + { + 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; + + bool result = PrintInstructions (disasm_sp.get(), + debugger, + arch, + exe_ctx, + num_instructions, + num_mixed_context_lines, + options, + strm); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + return result; + } + } + return false; +} + +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, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm +) +{ + if (num_instructions > 0) + { + lldb::DisassemblerSP disasm_sp (Disassembler::FindPluginForTarget(exe_ctx.GetTargetSP(), + arch, + flavor, + plugin_name)); + if (disasm_sp.get()) + { + 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; + bool result = PrintInstructions (disasm_sp.get(), + debugger, + arch, + exe_ctx, + num_instructions, + num_mixed_context_lines, + options, + strm); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + return result; + } + } + return false; +} + +bool +Disassembler::PrintInstructions +( + Disassembler *disasm_ptr, + Debugger &debugger, + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + 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 (); + uint32_t offset = 0; + SymbolContext sc; + SymbolContext prev_sc; + AddressRange sc_range; + const Address *pc_addr_ptr = NULL; + ExecutionContextScope *exe_scope = exe_ctx.GetBestExecutionContextScope(); + 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; + 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; + + 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 (num_mixed_context_lines) + { + if (!sc_range.ContainsFileAddress (addr)) + { + sc.GetAddressRange (scope, 0, use_inline_block_range, sc_range); + + if (sc != prev_sc) + { + if (offset != 0) + strm.EOL(); + + sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false); + strm.EOL(); + + if (sc.comp_unit && sc.line_entry.IsValid()) + { + source_manager.DisplaySourceLinesWithLineNumbers (sc.line_entry.file, + sc.line_entry.line, + num_mixed_context_lines, + num_mixed_context_lines, + ((inst_is_at_pc && (options & eOptionMarkPCSourceLine)) ? "->" : ""), + &strm); + } + } + } + } + else if ((sc.function || sc.symbol) && (sc.function != prev_sc.function || sc.symbol != prev_sc.symbol)) + { + if (prev_sc.function || prev_sc.symbol) + strm.EOL(); + + bool show_fullpaths = false; + bool show_module = true; + bool show_inlined_frames = true; + sc.DumpStopContext (&strm, + exe_scope, + addr, + show_fullpaths, + show_module, + show_inlined_frames); + + strm << ":\n"; + } + } + else + { + sc.Clear(true); + } + } + + if ((options & eOptionMarkPCAddress) && pc_addr_ptr) + { + strm.PutCString(inst_is_at_pc ? "-> " : " "); + } + const bool show_bytes = (options & eOptionShowBytes) != 0; + inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx); + 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, + 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->GetAddress(); + 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, + 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() +{ +} + +AddressClass +Instruction::GetAddressClass () +{ + if (m_address_class == eAddressClassInvalid) + 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) +{ + size_t opcode_column_width = 7; + const size_t operand_column_width = 25; + + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + + StreamString ss; + + if (show_address) + { + m_address.Dump(&ss, + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, + Address::DumpStyleLoadAddress, + Address::DumpStyleModuleWithFileAddress, + 0); + + ss.PutCString(": "); + } + + 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 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.GetSize(); + + // 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.c_str()); + ss.FillLastLineToColumn (opcode_pos + opcode_column_width, ' '); + ss.PutCString (m_mnemonics.c_str()); + + if (!m_comment.empty()) + { + ss.FillLastLineToColumn (opcode_pos + opcode_column_width + operand_column_width, ' '); + ss.PutCString (" ; "); + ss.PutCString (m_comment.c_str()); + } + s->Write (ss.GetData(), ss.GetSize()); +} + +bool +Instruction::DumpEmulation (const ArchSpec &arch) +{ + std::unique_ptr<EmulateInstruction> insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + { + insn_emulator_ap->SetInstruction (GetOpcode(), GetAddress(), NULL); + return insn_emulator_ap->EvaluateInstruction (0); + } + + return false; +} + +OptionValueSP +Instruction::ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type) +{ + bool done = false; + char buffer[1024]; + + OptionValueSP option_value_sp (new 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.size() > 0) + { + std::string value; + static RegularExpression g_reg_exp ("^[ \t]*([^ \t]+)[ \t]*$"); + RegularExpression::Match regex_match(1); + bool reg_exp_success = g_reg_exp.Execute (line.c_str(), ®ex_match); + if (reg_exp_success) + regex_match.GetMatchAtIndex (line.c_str(), 1, value); + else + value = line; + + OptionValueSP data_value_sp; + switch (data_type) + { + case OptionValue::eTypeUInt64: + data_value_sp.reset (new OptionValueUInt64 (0, 0)); + data_value_sp->SetValueFromCString (value.c_str()); + break; + // Other types can be added later as needed. + default: + data_value_sp.reset (new 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]; + + OptionValueSP option_value_sp (new 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.size() > 0) + { + static RegularExpression g_reg_exp ("^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$"); + RegularExpression::Match regex_match(2); + + bool reg_exp_success = g_reg_exp.Execute (line.c_str(), ®ex_match); + std::string key; + std::string value; + if (reg_exp_success) + { + regex_match.GetMatchAtIndex (line.c_str(), 1, key); + regex_match.GetMatchAtIndex (line.c_str(), 2, value); + } + 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.get() == NULL) + { + 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.get() == NULL) + { + 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.reset (new OptionValueUInt64 (0, 0)); + value_sp->SetValueFromCString (value.c_str()); + } + else + { + size_t len = value.size(); + if ((value[0] == '"') && (value[len-1] == '"')) + value = value.substr (1, len-2); + value_sp.reset (new 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 = 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.get() == NULL) + { + 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.get() == NULL) + { + 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.get() == NULL) + { + 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_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + success = insn_emulator_ap->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_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + { + insn_emulator_ap->SetBaton (baton); + insn_emulator_ap->SetCallbacks (read_mem_callback, write_mem_callback, read_reg_callback, write_reg_callback); + insn_emulator_ap->SetInstruction (GetOpcode(), GetAddress(), NULL); + return insn_emulator_ap->EvaluateInstruction (evaluate_options); + } + + return false; +} + + +uint32_t +Instruction::GetData (DataExtractor &data) +{ + return m_opcode.GetData(data); +} + +InstructionList::InstructionList() : + m_instructions() +{ +} + +InstructionList::~InstructionList() +{ +} + +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; + 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); + } +} + + +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) const +{ + size_t num_instructions = m_instructions.size(); + + uint32_t next_branch = UINT32_MAX; + for (size_t i = start; i < num_instructions; i++) + { + if (m_instructions[i]->DoesBranch()) + { + next_branch = i; + break; + } + } + return next_branch; +} + +uint32_t +InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +{ + Address address; + address.SetLoadAddress(load_addr, &target); + 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; +} + +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 == NULL || byte_size == 0 || !range.GetBaseAddress().IsValid()) + return 0; + + DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); + DataBufferSP data_sp(heap_buffer); + + Error error; + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + const size_t bytes_read = target->ReadMemory (range.GetBaseAddress(), + prefer_file_cache, + heap_buffer->GetBytes(), + heap_buffer->GetByteSize(), + error, + &load_addr); + + if (bytes_read > 0) + { + if (bytes_read != heap_buffer->GetByteSize()) + heap_buffer->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 == NULL || 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 == NULL || byte_size == 0) + return 0; + + DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); + DataBufferSP data_sp (heap_buffer); + + Error 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 == NULL) + m_flavor.assign("default"); + else + m_flavor.assign(flavor); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Disassembler::~Disassembler() +{ +} + +InstructionList & +Disassembler::GetInstructionList () +{ + return m_instruction_list; +} + +const InstructionList & +Disassembler::GetInstructionList () const +{ + return m_instruction_list; +} + +//---------------------------------------------------------------------- +// Class PseudoInstruction +//---------------------------------------------------------------------- +PseudoInstruction::PseudoInstruction () : + Instruction (Address(), eAddressClassUnknown), + m_description () +{ +} + +PseudoInstruction::~PseudoInstruction () +{ +} + +bool +PseudoInstruction::DoesBranch () +{ + // 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); + break; + } + case 16: + { + uint16_t value16 = *((uint16_t *) opcode_data); + m_opcode.SetOpcode16 (value16); + break; + } + case 32: + { + uint32_t value32 = *((uint32_t *) opcode_data); + m_opcode.SetOpcode32 (value32); + break; + } + case 64: + { + uint64_t value64 = *((uint64_t *) opcode_data); + m_opcode.SetOpcode64 (value64); + break; + } + default: + break; + } +} + +void +PseudoInstruction::SetDescription (const char *description) +{ + if (description && strlen (description) > 0) + m_description = description; +} diff --git a/source/Core/DynamicLoader.cpp b/source/Core/DynamicLoader.cpp new file mode 100644 index 0000000..82f8404 --- /dev/null +++ b/source/Core/DynamicLoader.cpp @@ -0,0 +1,76 @@ +//===-- DynamicLoader.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +DynamicLoader* +DynamicLoader::FindPlugin (Process *process, const char *plugin_name) +{ + DynamicLoaderCreateInstance create_callback = NULL; + if (plugin_name) + { + ConstString const_plugin_name(plugin_name); + create_callback = PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + std::unique_ptr<DynamicLoader> instance_ap(create_callback(process, true)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr<DynamicLoader> instance_ap(create_callback(process, false)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// DynamicLoader constructor +//---------------------------------------------------------------------- +DynamicLoader::DynamicLoader(Process *process) : + m_process (process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoader::~DynamicLoader() +{ +} + +//---------------------------------------------------------------------- +// 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); +} + diff --git a/source/Core/EmulateInstruction.cpp b/source/Core/EmulateInstruction.cpp new file mode 100644 index 0000000..bf6c6d8 --- /dev/null +++ b/source/Core/EmulateInstruction.cpp @@ -0,0 +1,670 @@ +//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/EmulateInstruction.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +EmulateInstruction* +EmulateInstruction::FindPlugin (const ArchSpec &arch, InstructionType supported_inst_type, const char *plugin_name) +{ + EmulateInstructionCreateInstance create_callback = NULL; + 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)) != NULL; ++idx) + { + EmulateInstruction *emulate_insn_ptr = create_callback(arch, supported_inst_type); + if (emulate_insn_ptr) + return emulate_insn_ptr; + } + } + return NULL; +} + +EmulateInstruction::EmulateInstruction (const ArchSpec &arch) : + m_arch (arch), + m_baton (NULL), + m_read_mem_callback (&ReadMemoryDefault), + m_write_mem_callback (&WriteMemoryDefault), + m_read_reg_callback (&ReadRegisterDefault), + m_write_reg_callback (&WriteRegisterDefault), + m_addr (LLDB_INVALID_ADDRESS) +{ + ::memset (&m_opcode, 0, sizeof (m_opcode)); +} + + +bool +EmulateInstruction::ReadRegister (const RegisterInfo *reg_info, RegisterValue& reg_value) +{ + if (m_read_reg_callback) + return m_read_reg_callback (this, m_baton, reg_info, reg_value); + return false; +} + +bool +EmulateInstruction::ReadRegister (uint32_t reg_kind, uint32_t reg_num, RegisterValue& reg_value) +{ + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return ReadRegister (®_info, reg_value); + return false; +} + +uint64_t +EmulateInstruction::ReadRegisterUnsigned (uint32_t reg_kind, + uint32_t reg_num, + uint64_t fail_value, + bool *success_ptr) +{ + RegisterValue reg_value; + if (ReadRegister (reg_kind, reg_num, reg_value)) + return reg_value.GetAsUInt64(fail_value, success_ptr); + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint64_t +EmulateInstruction::ReadRegisterUnsigned (const RegisterInfo *reg_info, + uint64_t fail_value, + bool *success_ptr) +{ + RegisterValue reg_value; + if (ReadRegister (reg_info, reg_value)) + return reg_value.GetAsUInt64(fail_value, success_ptr); + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +bool +EmulateInstruction::WriteRegister (const Context &context, + const RegisterInfo *reg_info, + const RegisterValue& reg_value) +{ + if (m_write_reg_callback) + return m_write_reg_callback (this, m_baton, context, reg_info, reg_value); + return false; +} + +bool +EmulateInstruction::WriteRegister (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + const RegisterValue& reg_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, + uint32_t 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) + { + 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) + 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.GetData(), uval_byte_size); + if (bytes_written == uval_byte_size) + return true; + return false; +} + +bool +EmulateInstruction::WriteMemory (const Context &context, + lldb::addr_t addr, + const void *src, + size_t src_len) +{ + if (m_write_mem_callback) + 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 || dst == NULL || dst_len == 0) + return 0; + + StackFrame *frame = (StackFrame *) baton; + + ProcessSP process_sp (frame->CalculateProcess()); + if (process_sp) + { + Error 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 || src == NULL || src_len == 0) + return 0; + + StackFrame *frame = (StackFrame *) baton; + + ProcessSP process_sp (frame->CalculateProcess()); + if (process_sp) + { + Error 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) + 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) + 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); + uint32_t reg_kind, 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); + reg_value.Dump(&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) + 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, + uint32_t ®_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[eRegisterKindGCC]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGCC; + return true; + } + + reg_num = reg_info->kinds[eRegisterKindGDB]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGDB; + return true; + } + return false; +} + +uint32_t +EmulateInstruction::GetInternalRegisterNumber (RegisterContext *reg_ctx, const RegisterInfo ®_info) +{ + uint32_t reg_kind, 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/source/Core/Error.cpp b/source/Core/Error.cpp new file mode 100644 index 0000000..e29f12f --- /dev/null +++ b/source/Core/Error.cpp @@ -0,0 +1,399 @@ +//===-- Error.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <errno.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "llvm/ADT/SmallVector.h" +#include <cstdarg> +#include <cstdlib> +#include <cstring> + +#if defined (__arm__) && defined (__APPLE__) +#include <SpringBoardServices/SpringBoardServer.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +Error::Error (): + m_code (0), + m_type (eErrorTypeInvalid), + m_string () +{ +} + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Error::Error(ValueType err, ErrorType type) : + m_code (err), + m_type (type), + m_string () +{ +} + +Error::Error (const Error &rhs) : + m_code (rhs.m_code), + m_type (rhs.m_type), + m_string (rhs.m_string) +{ +} + +Error::Error (const char* err_str): + m_code (0), + m_type (eErrorTypeInvalid), + m_string () +{ + SetErrorString(err_str); +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Error& +Error::operator = (const Error& rhs) +{ + if (this != &rhs) + { + m_code = rhs.m_code; + m_type = rhs.m_type; + m_string = rhs.m_string; + } + return *this; +} + + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Error& +Error::operator = (uint32_t err) +{ + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); + return *this; +} + +Error::~Error() +{ +} + +//---------------------------------------------------------------------- +// Get the error value as a NULL C string. The error string will be +// fetched and cached on demand. The cached error string value will +// remain until the error value is changed or cleared. +//---------------------------------------------------------------------- +const char * +Error::AsCString(const char *default_error_str) const +{ + if (Success()) + return NULL; + + if (m_string.empty()) + { + const char *s = NULL; + switch (m_type) + { + case eErrorTypeMachKernel: +#if defined (__APPLE__) + s = ::mach_error_string (m_code); +#endif + break; + + case eErrorTypePOSIX: + s = ::strerror (m_code); + break; + + default: + break; + } + if (s) + m_string.assign(s); + } + if (m_string.empty()) + { + if (default_error_str) + m_string.assign(default_error_str); + else + return NULL; // User wanted a NULL string back... + } + return m_string.c_str(); +} + + +//---------------------------------------------------------------------- +// Clear the error and any cached error string that it might contain. +//---------------------------------------------------------------------- +void +Error::Clear () +{ + m_code = 0; + m_type = eErrorTypeGeneric; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Access the error value. +//---------------------------------------------------------------------- +Error::ValueType +Error::GetError () const +{ + return m_code; +} + +//---------------------------------------------------------------------- +// Access the error type. +//---------------------------------------------------------------------- +ErrorType +Error::GetType () const +{ + return m_type; +} + +//---------------------------------------------------------------------- +// Retuns true if this object contains an value that describes an +// error or otherwise non-success result. +//---------------------------------------------------------------------- +bool +Error::Fail () const +{ + return m_code != 0; +} + +//---------------------------------------------------------------------- +// Log the error given a string with format. If the this object +// contains an error code, update the error string to contain the +// "error: " followed by the formatted string, followed by the error +// value and any string that describes the current error. This +// allows more context to be given to an error string that remains +// cached in this object. Logging always occurs even when the error +// code contains a non-error value. +//---------------------------------------------------------------------- +void +Error::PutToLog (Log *log, const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + if (Fail()) + { + const char *err_str = AsCString(); + if (err_str == NULL) + err_str = "???"; + + SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + if (log) + log->Error("%s", m_string.c_str()); + } + else + { + if (log) + log->Printf("%s err = 0x%8.8x", arg_msg, m_code); + } + ::free (arg_msg); + } +} + +//---------------------------------------------------------------------- +// Log the error given a string with format. If the this object +// contains an error code, update the error string to contain the +// "error: " followed by the formatted string, followed by the error +// value and any string that describes the current error. This +// allows more context to be given to an error string that remains +// cached in this object. Logging only occurs even when the error +// code contains a error value. +//---------------------------------------------------------------------- +void +Error::LogIfError (Log *log, const char *format, ...) +{ + if (Fail()) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + const char *err_str = AsCString(); + if (err_str == NULL) + err_str = "???"; + + SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + if (log) + log->Error("%s", m_string.c_str()); + + ::free (arg_msg); + } + } +} + +//---------------------------------------------------------------------- +// Set accesssor for the error value to "err" and the type to +// "eErrorTypeMachKernel" +//---------------------------------------------------------------------- +void +Error::SetMachError (uint32_t err) +{ + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Set accesssor for the error value and type. +//---------------------------------------------------------------------- +void +Error::SetError (ValueType err, ErrorType type) +{ + m_code = err; + m_type = type; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Update the error value to be "errno" and update the type to +// be "POSIX". +//---------------------------------------------------------------------- +void +Error::SetErrorToErrno() +{ + m_code = errno; + m_type = eErrorTypePOSIX; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Update the error value to be LLDB_GENERIC_ERROR and update the type +// to be "Generic". +//---------------------------------------------------------------------- +void +Error::SetErrorToGenericError () +{ + m_code = LLDB_GENERIC_ERROR; + m_type = eErrorTypeGeneric; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Set accessor for the error string value for a specific error. +// This allows any string to be supplied as an error explanation. +// The error string value will remain until the error value is +// cleared or a new error value/type is assigned. +//---------------------------------------------------------------------- +void +Error::SetErrorString (const char *err_str) +{ + if (err_str && err_str[0]) + { + // If we have an error string, we should always at least have + // an error set to a generic value. + if (Success()) + SetErrorToGenericError(); + m_string = err_str; + } + else + m_string.clear(); +} + +//------------------------------------------------------------------ +/// Set the current error string to a formatted error string. +/// +/// @param format +/// A printf style format string +//------------------------------------------------------------------ +int +Error::SetErrorStringWithFormat (const char *format, ...) +{ + if (format && format[0]) + { + va_list args; + va_start (args, format); + int length = SetErrorStringWithVarArg (format, args); + va_end (args); + return length; + } + else + { + m_string.clear(); + } + return 0; +} + +int +Error::SetErrorStringWithVarArg (const char *format, va_list args) +{ + if (format && format[0]) + { + // If we have an error string, we should always at least have + // an error set to a generic value. + if (Success()) + SetErrorToGenericError(); + + // Try and fit our error into a 1024 byte buffer first... + llvm::SmallVector<char, 1024> buf; + buf.resize(1024); + // Copy in case our first call to vsnprintf doesn't fit into our + // allocated buffer above + va_list copy_args; + va_copy (copy_args, args); + unsigned length = ::vsnprintf (buf.data(), buf.size(), format, args); + if (length >= buf.size()) + { + // The error formatted string didn't fit into our buffer, resize it + // to the exact needed size, and retry + buf.resize(length + 1); + length = ::vsnprintf (buf.data(), buf.size(), format, copy_args); + va_end (copy_args); + assert (length < buf.size()); + } + m_string.assign(buf.data(), length); + va_end (args); + return length; + } + else + { + m_string.clear(); + } + return 0; +} + + +//---------------------------------------------------------------------- +// Returns true if the error code in this object is considered a +// successful return value. +//---------------------------------------------------------------------- +bool +Error::Success() const +{ + return m_code == 0; +} + +bool +Error::WasInterrupted() const +{ + if (m_type == eErrorTypePOSIX && m_code == EINTR) + return true; + else + return false; +} + diff --git a/source/Core/Event.cpp b/source/Core/Event.cpp new file mode 100644 index 0000000..2d4899d --- /dev/null +++ b/source/Core/Event.cpp @@ -0,0 +1,225 @@ +//===-- Event.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Event.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" +#include "lldb/Target/Process.h" +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Event constructor +//---------------------------------------------------------------------- +Event::Event (Broadcaster *broadcaster, uint32_t event_type, EventData *data) : + m_broadcaster (broadcaster), + m_type (event_type), + m_data_ap (data) +{ +} + +Event::Event(uint32_t event_type, EventData *data) : + m_broadcaster (NULL), // Set by the broadcaster when this event gets broadcast + m_type (event_type), + m_data_ap (data) +{ +} + + +//---------------------------------------------------------------------- +// Event destructor +//---------------------------------------------------------------------- +Event::~Event () +{ +} + +void +Event::Dump (Stream *s) const +{ + if (m_broadcaster) + { + StreamString event_name; + if (m_broadcaster->GetEventNames (event_name, m_type, false)) + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x (%s), data = ", + this, + m_broadcaster, + m_broadcaster->GetBroadcasterName().GetCString(), + m_type, + event_name.GetString().c_str()); + else + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x, data = ", + this, + m_broadcaster, + m_broadcaster->GetBroadcasterName().GetCString(), + m_type); + } + else + s->Printf("%p Event: broadcaster = NULL, type = 0x%8.8x, data = ", this, m_type); + + if (m_data_ap.get() == NULL) + s->Printf ("<NULL>"); + else + { + s->PutChar('{'); + m_data_ap->Dump (s); + s->PutChar('}'); + } +} + +void +Event::DoOnRemoval () +{ + if (m_data_ap.get()) + m_data_ap->DoOnRemoval (this); +} + +EventData::EventData() +{ +} + +EventData::~EventData() +{ +} + +void +EventData::Dump (Stream *s) const +{ + s->PutCString ("Generic Event Data"); +} + +EventDataBytes::EventDataBytes () : + m_bytes() +{ +} + +EventDataBytes::EventDataBytes (const char *cstr) : + m_bytes() +{ + SetBytesFromCString (cstr); +} + +EventDataBytes::EventDataBytes (const void *src, size_t src_len) : + m_bytes() +{ + SetBytes (src, src_len); +} + +EventDataBytes::~EventDataBytes() +{ +} + +const ConstString & +EventDataBytes::GetFlavorString () +{ + static ConstString g_flavor ("EventDataBytes"); + return g_flavor; +} + +const ConstString & +EventDataBytes::GetFlavor () const +{ + return EventDataBytes::GetFlavorString (); +} + +void +EventDataBytes::Dump (Stream *s) const +{ + size_t num_printable_chars = std::count_if (m_bytes.begin(), m_bytes.end(), isprint); + if (num_printable_chars == m_bytes.size()) + { + s->Printf("\"%s\"", m_bytes.c_str()); + } + else if (m_bytes.size() > 0) + { + DataExtractor data; + data.SetData(&m_bytes[0], m_bytes.size(), lldb::endian::InlHostByteOrder()); + data.Dump(s, 0, eFormatBytes, 1, m_bytes.size(), 32, LLDB_INVALID_ADDRESS, 0, 0); + } +} + +const void * +EventDataBytes::GetBytes() const +{ + if (m_bytes.empty()) + return NULL; + return &m_bytes[0]; +} + +size_t +EventDataBytes::GetByteSize() const +{ + return m_bytes.size (); +} + +void +EventDataBytes::SetBytes (const void *src, size_t src_len) +{ + if (src && src_len > 0) + m_bytes.assign ((const char *)src, src_len); + else + m_bytes.clear(); +} + +void +EventDataBytes::SetBytesFromCString (const char *cstr) +{ + if (cstr && cstr[0]) + m_bytes.assign (cstr); + else + m_bytes.clear(); +} + + +const void * +EventDataBytes::GetBytesFromEvent (const Event *event_ptr) +{ + const EventDataBytes *e = GetEventDataFromEvent (event_ptr); + if (e) + return e->GetBytes(); + return NULL; +} + +size_t +EventDataBytes::GetByteSizeFromEvent (const Event *event_ptr) +{ + const EventDataBytes *e = GetEventDataFromEvent (event_ptr); + if (e) + return e->GetByteSize(); + return 0; +} + +const EventDataBytes * +EventDataBytes::GetEventDataFromEvent (const Event *event_ptr) +{ + if (event_ptr) + { + const EventData *event_data = event_ptr->GetData(); + if (event_data && event_data->GetFlavor() == EventDataBytes::GetFlavorString()) + return static_cast <const EventDataBytes *> (event_data); + } + return NULL; +} + +void +EventDataBytes::SwapBytes (std::string &new_bytes) +{ + m_bytes.swap (new_bytes); +} + + diff --git a/source/Core/FileLineResolver.cpp b/source/Core/FileLineResolver.cpp new file mode 100644 index 0000000..15cbbe6 --- /dev/null +++ b/source/Core/FileLineResolver.cpp @@ -0,0 +1,117 @@ +//===-- FileLineResolver.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/FileLineResolver.h" + +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" + +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, + bool containing +) +{ + CompileUnit *cu = context.comp_unit; + + if (m_inlines || m_file_spec.Compare(*cu, m_file_spec, 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; +} + +Searcher::Depth +FileLineResolver::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +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/source/Core/FileSpecList.cpp b/source/Core/FileSpecList.cpp new file mode 100644 index 0000000..0cec8fa --- /dev/null +++ b/source/Core/FileSpecList.cpp @@ -0,0 +1,234 @@ +//===-- FileSpecList.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Stream.h" +#include <algorithm> + +using namespace lldb_private; +using namespace std; + +//------------------------------------------------------------------ +// Default constructor +//------------------------------------------------------------------ +FileSpecList::FileSpecList() : + m_files() +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpecList::FileSpecList(const FileSpecList& rhs) : + m_files(rhs.m_files) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +FileSpecList::~FileSpecList() +{ +} + +//------------------------------------------------------------------ +// Assignment operator +//------------------------------------------------------------------ +const FileSpecList& +FileSpecList::operator= (const FileSpecList& rhs) +{ + if (this != &rhs) + m_files = rhs.m_files; + return *this; +} + +//------------------------------------------------------------------ +// 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 pos, 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 UINT32_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 (m_files[idx].GetFilename() == file_spec.GetFilename()) + 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 NULL; +} + +//------------------------------------------------------------------ +// 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) +{ +#if 0 // FIXME: Just sketching... + matches.Clear(); + FileSpec path_spec = FileSpec (path); + if (path_spec.Exists ()) + { + FileSpec::FileType type = path_spec.GetFileType(); + if (type == FileSpec::eFileTypeSymbolicLink) + // Shouldn't there be a Resolve on a file spec that real-path's it? + { + } + + if (type == FileSpec::eFileTypeRegular + || (type == FileSpec::eFileTypeDirectory && dir_okay)) + { + matches.Append (path_spec); + return 1; + } + else if (type == FileSpec::eFileTypeDirectory) + { + // Fill the match list with all the files in the directory: + + } + else + { + return 0; + } + + } + else + { + ConstString dir_name = path_spec.GetDirectory(); + Constring file_name = GetFilename(); + if (dir_name == NULL) + { + // Match files in the CWD. + } + else + { + // Match files in the given directory: + + } + } +#endif + return 0; +} diff --git a/source/Core/History.cpp b/source/Core/History.cpp new file mode 100644 index 0000000..0105dce --- /dev/null +++ b/source/Core/History.cpp @@ -0,0 +1,26 @@ +//===-- History.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/History.h" + +// C Includes +#include <inttypes.h> +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +HistorySourceUInt::DumpHistoryEvent (Stream &strm, HistoryEvent event) +{ + strm.Printf ("%s %" PRIu64, m_name.c_str(), (uint64_t)((uintptr_t)event)); +} diff --git a/source/Core/InputReader.cpp b/source/Core/InputReader.cpp new file mode 100644 index 0000000..cbaa671 --- /dev/null +++ b/source/Core/InputReader.cpp @@ -0,0 +1,387 @@ +//===-- InputReader.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include <string> + +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +InputReader::InputReader (Debugger &debugger) : + m_debugger (debugger), + m_callback (NULL), + m_callback_baton (NULL), + m_end_token (), + m_granularity (eInputReaderGranularityInvalid), + m_done (true), + m_echo (true), + m_active (false), + m_reader_done (false), + m_user_input(), + m_save_user_input(false) +{ +} + +InputReader::~InputReader () +{ +} + +Error +InputReader::Initialize +( + Callback callback, + void *baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + Error err; + m_callback = callback; + m_callback_baton = baton, + m_granularity = granularity; + if (end_token != NULL) + m_end_token = end_token; + if (prompt != NULL) + m_prompt = prompt; + m_done = true; + m_echo = echo; + + if (m_granularity == eInputReaderGranularityInvalid) + { + err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'."); + } + else + if (end_token != NULL && granularity != eInputReaderGranularityInvalid) + { + if (granularity == eInputReaderGranularityByte) + { + // Check to see if end_token is longer than one byte. + + if (strlen (end_token) > 1) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte)."); + } + } + else if (granularity == eInputReaderGranularityWord) + { + // Check to see if m_end_token contains any white space (i.e. is multiple words). + + const char *white_space = " \t\n"; + size_t pos = m_end_token.find_first_of (white_space); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word)."); + } + } + else + { + // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens. + + size_t pos = m_end_token.find_first_of ('\n'); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot contain a newline."); + } + } + } + + m_done = err.Fail(); + + return err; +} + +size_t +InputReader::HandleRawBytes (const char *bytes, size_t bytes_len) +{ + const char *end_token = NULL; + + if (m_end_token.empty() == false) + { + end_token = ::strstr (bytes, m_end_token.c_str()); + if (end_token >= bytes + bytes_len) + end_token = NULL; + } + + const char *p = bytes; + const char *end = bytes + bytes_len; + + switch (m_granularity) + { + case eInputReaderGranularityInvalid: + break; + + case eInputReaderGranularityByte: + while (p < end) + { + if (end_token == p) + { + p += m_end_token.size(); + SetIsDone(true); + break; + } + + if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0) + break; + ++p; + if (IsDone()) + break; + } + // Return how many bytes were handled. + return p - bytes; + break; + + + case eInputReaderGranularityWord: + { + char quote = '\0'; + const char *word_start = NULL; + bool send_word = false; + for (; p < end; ++p, send_word = false) + { + if (end_token && end_token == p) + { + m_end_token.size(); + SetIsDone(true); + break; + } + + const char ch = *p; + if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\'))) + { + // We have a space character or the terminating quote + send_word = word_start != NULL; + quote = '\0'; + } + else if (quote) + { + // We are in the middle of a quoted character + continue; + } + else if (ch == '"' || ch == '\'' || ch == '`') + quote = ch; + else if (word_start == NULL) + { + // We have the first character in a word + word_start = p; + } + + if (send_word) + { + const size_t word_len = p - word_start; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + word_start, + word_len); + + if (bytes_handled != word_len) + return word_start - bytes + bytes_handled; + + if (IsDone()) + return p - bytes; + } + } + } + break; + + + case eInputReaderGranularityLine: + { + const char *line_start = bytes; + const char *end_line = NULL; + while (p < end) + { + const char ch = *p; + if (ch == '\n' || ch == '\r') + { + size_t line_length = p - line_start; + // Now skip the newline character + ++p; + // Skip a complete DOS newline if we run into one + if (ch == 0xd && p < end && *p == 0xa) + ++p; + + if (line_start <= end_token && end_token < line_start + line_length) + { + SetIsDone(true); + m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + end_token - line_start); + + return p - bytes; + } + + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + line_length); + + end_line = p; + + if (bytes_handled != line_length) + { + // The input reader wasn't able to handle all the data + return line_start - bytes + bytes_handled; + } + + + if (IsDone()) + return p - bytes; + + line_start = p; + } + else + { + ++p; + } + } + + if (end_line) + return end_line - bytes; + } + break; + + + case eInputReaderGranularityAll: + { + // Nothing should be handle unless we see our end token + if (end_token) + { + size_t length = end_token - bytes; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + bytes, + length); + m_done = true; + + p += bytes_handled + m_end_token.size(); + + // Consume any white space, such as newlines, beyond the end token + + while (p < end && isspace(*p)) + ++p; + + if (bytes_handled != length) + return bytes_handled; + else + { + return p - bytes; + //return bytes_handled + m_end_token.size(); + } + } + return 0; + } + break; + } + return 0; +} + +const char * +InputReader::GetPrompt () const +{ + if (!m_prompt.empty()) + return m_prompt.c_str(); + else + return NULL; +} + +void +InputReader::RefreshPrompt () +{ + if (m_debugger.GetCommandInterpreter().GetBatchCommandMode()) + return; + + if (!m_prompt.empty()) + { + File &out_file = m_debugger.GetOutputFile(); + if (out_file.IsValid()) + { + out_file.Printf ("%s", m_prompt.c_str()); + out_file.Flush(); + } + } +} + +void +InputReader::Notify (InputReaderAction notification) +{ + switch (notification) + { + case eInputReaderActivate: + case eInputReaderReactivate: + m_active = true; + m_reader_done.SetValue(false, eBroadcastAlways); + break; + + case eInputReaderDeactivate: + case eInputReaderDone: + m_active = false; + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + break; + + case eInputReaderGotToken: + return; // We don't notify the tokens here, it is done in HandleRawBytes + } + if (m_callback) + m_callback (m_callback_baton, *this, notification, NULL, 0); + if (notification == eInputReaderDone) + m_reader_done.SetValue(true, eBroadcastAlways); +} + +void +InputReader::WaitOnReaderIsDone () +{ + m_reader_done.WaitForValueEqualTo (true); +} + +const char * +InputReader::GranularityAsCString (lldb::InputReaderGranularity granularity) +{ + switch (granularity) + { + case eInputReaderGranularityInvalid: return "invalid"; + case eInputReaderGranularityByte: return "byte"; + case eInputReaderGranularityWord: return "word"; + case eInputReaderGranularityLine: return "line"; + case eInputReaderGranularityAll: return "all"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "InputReaderGranularity = %i", granularity); + return unknown_state_string; +} + +bool +InputReader::HandlerData::GetBatchMode() +{ + return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); +} + +lldb::StreamSP +InputReader::HandlerData::GetOutStream() +{ + return reader.GetDebugger().GetAsyncOutputStream(); +} diff --git a/source/Core/InputReaderEZ.cpp b/source/Core/InputReaderEZ.cpp new file mode 100644 index 0000000..7a865bd --- /dev/null +++ b/source/Core/InputReaderEZ.cpp @@ -0,0 +1,91 @@ +//===-- InputReaderEZ.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/InputReaderEZ.h" + +using namespace lldb; +using namespace lldb_private; + +size_t +InputReaderEZ::Callback_Impl(void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + +{ + HandlerData hand_data(reader, + bytes, + bytes_len, + baton); + + switch (notification) + { + case eInputReaderActivate: + reader.ActivateHandler(hand_data); + break; + case eInputReaderDeactivate: + reader.DeactivateHandler(hand_data); + break; + case eInputReaderReactivate: + reader.ReactivateHandler(hand_data); + break; + case eInputReaderAsynchronousOutputWritten: + reader.AsynchronousOutputWrittenHandler(hand_data); + break; + case eInputReaderGotToken: + { + if (reader.GetSaveUserInput()) + reader.GetUserInput().AppendString(bytes, bytes_len); + reader.GotTokenHandler(hand_data); + } + break; + case eInputReaderInterrupt: + reader.InterruptHandler(hand_data); + break; + case eInputReaderEndOfFile: + reader.EOFHandler(hand_data); + break; + case eInputReaderDone: + reader.DoneHandler(hand_data); + break; + } + return bytes_len; +} + +Error +InputReaderEZ::Initialize(void* baton, + lldb::InputReaderGranularity token_size, + const char* end_token, + const char *prompt, + bool echo) +{ + return InputReader::Initialize(Callback_Impl, + baton, + token_size, + end_token, + prompt, + echo); +} + +Error +InputReaderEZ::Initialize(InitializationParameters& params) +{ + Error ret = Initialize(params.m_baton, + params.m_token_size, + params.m_end_token, + params.m_prompt, + params.m_echo); + m_save_user_input = params.m_save_user_input; + return ret; +} + +InputReaderEZ::~InputReaderEZ () +{ +} diff --git a/source/Core/InputReaderStack.cpp b/source/Core/InputReaderStack.cpp new file mode 100644 index 0000000..764ea26 --- /dev/null +++ b/source/Core/InputReaderStack.cpp @@ -0,0 +1,80 @@ +//===-- InputReaderStack.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/InputReaderStack.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + + +using namespace lldb; +using namespace lldb_private; + +InputReaderStack::InputReaderStack () : + m_input_readers (), + m_input_readers_mutex (Mutex::eMutexTypeRecursive) +{ +} + +InputReaderStack::~InputReaderStack () +{ +} + +size_t +InputReaderStack::GetSize () const +{ + Mutex::Locker locker (m_input_readers_mutex); + return m_input_readers.size(); +} + +void +InputReaderStack::Push (const lldb::InputReaderSP& reader_sp) +{ + if (reader_sp) + { + Mutex::Locker locker (m_input_readers_mutex); + m_input_readers.push (reader_sp); + } +} + +bool +InputReaderStack::IsEmpty () const +{ + Mutex::Locker locker (m_input_readers_mutex); + return m_input_readers.empty(); +} + +InputReaderSP +InputReaderStack::Top () +{ + InputReaderSP input_reader_sp; + { + Mutex::Locker locker (m_input_readers_mutex); + if (!m_input_readers.empty()) + input_reader_sp = m_input_readers.top(); + } + + return input_reader_sp; +} + +void +InputReaderStack::Pop () +{ + Mutex::Locker locker (m_input_readers_mutex); + if (!m_input_readers.empty()) + m_input_readers.pop(); +} + +Mutex & +InputReaderStack::GetStackMutex () +{ + return m_input_readers_mutex; +} diff --git a/source/Core/Language.cpp b/source/Core/Language.cpp new file mode 100644 index 0000000..af62af3 --- /dev/null +++ b/source/Core/Language.cpp @@ -0,0 +1,151 @@ +//===-- Language.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Core/Language.h" +#include "lldb/Core/Stream.h" +#include <string.h> + +using namespace lldb; +using namespace lldb_private; + +#define ENUM_TO_DCSTREAM(x) case x: s->PutCString(#x); return + +struct LanguageStrings +{ + const char * names[3]; +}; + +static LanguageStrings +g_languages[] = +{ + { { "unknown" , NULL , NULL } }, + { { "c89" , NULL , "ISO C:1989" } }, + { { NULL , NULL , "K&R C" } }, + { { "ada83" , "Ada83" , "ISO Ada:1983" } }, + { { "c++" , "cxx" , "ISO C++:1998" } }, + { { "cobol74" , "Cobol74" , "ISO Cobol:1974" } }, + { { "cobol" , "Cobol85" , "ISO Cobol:1985." } }, + { { "f77" , "Fortran77" , "ISO Fortran 77." } }, + { { "f90" , "Fortran90" , "ISO Fortran 90" } }, + { { "pascal" , "Pascal83" , "ISO Pascal:1983" } }, + { { "modula2" , "Modula2" , "ISO Modula-2:1996" } }, + { { "java" , NULL , "Java" } }, + { { "c" , "C99" , "ISO C:1999" } }, + { { "ada" , "Ada95" , "ISO Ada:1995" } }, + { { "f95" , "Fortran95" , "ISO Fortran 95" } }, + { { "PLI" , NULL , "ANSI PL/I:1976" } }, + { { "objc" , NULL , "Objective-C" } }, + { { "objc++" , NULL , "Objective-C++" } }, + { { "upc" , NULL , "Unified Parallel C" } }, + { { "d" , NULL , "D" } }, + { { "python" , NULL , "Python" } } +}; + +static const size_t +g_num_languages = sizeof(g_languages)/sizeof(LanguageStrings); + +Language::Language(LanguageType language) : + m_language (language) +{ +} + +Language::~Language() +{ +} + +LanguageType +Language::GetLanguage() const +{ + return m_language; +} + +void +Language::Clear () +{ + m_language = eLanguageTypeUnknown; +} + +void +Language::SetLanguage(LanguageType language) +{ + m_language = language; +} + +bool +Language::SetLanguageFromCString(const char *language_cstr) +{ + size_t i, desc_idx; + const char *name; + + // First check the most common name for the languages + for (desc_idx=lldb::eDescriptionLevelBrief; desc_idx<kNumDescriptionLevels; ++desc_idx) + { + for (i=0; i<g_num_languages; ++i) + { + name = g_languages[i].names[desc_idx]; + if (name == NULL) + continue; + + if (::strcasecmp (language_cstr, name) == 0) + { + m_language = (LanguageType)i; + return true; + } + } + } + + m_language = eLanguageTypeUnknown; + return false; +} + + +const char * +Language::AsCString (lldb::DescriptionLevel level) const +{ + if (m_language < g_num_languages && level < kNumDescriptionLevels) + { + const char *name = g_languages[m_language].names[level]; + if (name) + return name; + else if (level + 1 < kNumDescriptionLevels) + return AsCString ((lldb::DescriptionLevel)(level + 1)); + else + return NULL; + } + return NULL; +} + +void +Language::Dump(Stream *s) const +{ + GetDescription(s, lldb::eDescriptionLevelVerbose); +} + +void +Language::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + const char *lang_cstr = AsCString(level); + + if (lang_cstr) + s->PutCString(lang_cstr); + else + s->Printf("Language(language = 0x%4.4x)", m_language); +} + + + + +Stream& +lldb_private::operator << (Stream& s, const Language& language) +{ + language.Dump(&s); + return s; +} + diff --git a/source/Core/Listener.cpp b/source/Core/Listener.cpp new file mode 100644 index 0000000..aca2b37 --- /dev/null +++ b/source/Core/Listener.cpp @@ -0,0 +1,557 @@ +//===-- Listener.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Listener.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Event.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/lldb-private-log.h" +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +Listener::Listener(const char *name) : + m_name (name), + m_broadcasters(), + m_broadcasters_mutex (Mutex::eMutexTypeRecursive), + m_events (), + m_events_mutex (Mutex::eMutexTypeRecursive), + m_cond_wait() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Listener::Listener('%s')", this, m_name.c_str()); +} + +Listener::~Listener() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + Mutex::Locker locker (m_broadcasters_mutex); + + size_t num_managers = m_broadcaster_managers.size(); + + for (size_t i = 0; i < num_managers; i++) + m_broadcaster_managers[i]->RemoveListener(*this); + + if (log) + log->Printf ("%p Listener::~Listener('%s')", this, m_name.c_str()); + Clear(); +} + +void +Listener::Clear() +{ + Mutex::Locker locker(m_broadcasters_mutex); + broadcaster_collection::iterator pos, end = m_broadcasters.end(); + for (pos = m_broadcasters.begin(); pos != end; ++pos) + pos->first->RemoveListener (this, pos->second.event_mask); + m_broadcasters.clear(); + m_cond_wait.SetValue (false, eBroadcastNever); + m_broadcasters.clear(); + Mutex::Locker event_locker(m_events_mutex); + m_events.clear(); +} + +uint32_t +Listener::StartListeningForEvents (Broadcaster* broadcaster, uint32_t event_mask) +{ + if (broadcaster) + { + // Scope for "locker" + // Tell the broadcaster to add this object as a listener + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.insert(std::make_pair(broadcaster, BroadcasterInfo(event_mask))); + } + + uint32_t acquired_mask = broadcaster->AddListener (this, event_mask); + + if (event_mask != acquired_mask) + { + + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener::StartListeningForEvents (broadcaster = %p, mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s", + this, + broadcaster, + event_mask, + acquired_mask, + m_name.c_str()); + + return acquired_mask; + + } + return 0; +} + +uint32_t +Listener::StartListeningForEvents (Broadcaster* broadcaster, uint32_t event_mask, HandleBroadcastCallback callback, void *callback_user_data) +{ + if (broadcaster) + { + // Scope for "locker" + // Tell the broadcaster to add this object as a listener + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.insert(std::make_pair(broadcaster, BroadcasterInfo(event_mask, callback, callback_user_data))); + } + + uint32_t acquired_mask = broadcaster->AddListener (this, event_mask); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener::StartListeningForEvents (broadcaster = %p, mask = 0x%8.8x, callback = %p, user_data = %p) acquired_mask = 0x%8.8x for %s", + this, broadcaster, event_mask, callback, callback_user_data, acquired_mask, m_name.c_str()); + + return acquired_mask; + } + return 0; +} + +bool +Listener::StopListeningForEvents (Broadcaster* broadcaster, uint32_t event_mask) +{ + if (broadcaster) + { + // Scope for "locker" + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.erase (broadcaster); + } + // Remove the broadcaster from our set of broadcasters + return broadcaster->RemoveListener (this, event_mask); + } + + return false; +} + +// Called when a Broadcaster is in its destuctor. We need to remove all +// knowledge of this broadcaster and any events that it may have queued up +void +Listener::BroadcasterWillDestruct (Broadcaster *broadcaster) +{ + // Scope for "broadcasters_locker" + { + Mutex::Locker broadcasters_locker(m_broadcasters_mutex); + m_broadcasters.erase (broadcaster); + } + + // Scope for "event_locker" + { + Mutex::Locker event_locker(m_events_mutex); + // Remove all events for this broadcaster object. + event_collection::iterator pos = m_events.begin(); + while (pos != m_events.end()) + { + if ((*pos)->GetBroadcaster() == broadcaster) + pos = m_events.erase(pos); + else + ++pos; + } + + if (m_events.empty()) + m_cond_wait.SetValue (false, eBroadcastNever); + + } +} + +void +Listener::BroadcasterManagerWillDestruct (BroadcasterManager *manager) +{ + // Just need to remove this broadcast manager from the list of managers: + broadcaster_manager_collection::iterator iter, end_iter = m_broadcaster_managers.end(); + iter = find(m_broadcaster_managers.begin(), end_iter, manager); + if (iter != end_iter) + m_broadcaster_managers.erase (iter); +} + +void +Listener::AddEvent (EventSP &event_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener('%s')::AddEvent (event_sp = {%p})", this, m_name.c_str(), event_sp.get()); + + // Scope for "locker" + { + Mutex::Locker locker(m_events_mutex); + m_events.push_back (event_sp); + } + m_cond_wait.SetValue (true, eBroadcastAlways); +} + +class EventBroadcasterMatches +{ +public: + EventBroadcasterMatches (Broadcaster *broadcaster) : + m_broadcaster (broadcaster) { + } + + bool operator() (const EventSP &event_sp) const + { + if (event_sp->BroadcasterIs(m_broadcaster)) + return true; + else + return false; + } + +private: + Broadcaster *m_broadcaster; + +}; + +class EventMatcher +{ +public: + EventMatcher (Broadcaster *broadcaster, const ConstString *broadcaster_names, uint32_t num_broadcaster_names, uint32_t event_type_mask) : + m_broadcaster (broadcaster), + m_broadcaster_names (broadcaster_names), + m_num_broadcaster_names (num_broadcaster_names), + m_event_type_mask (event_type_mask) + { + } + + bool operator() (const EventSP &event_sp) const + { + if (m_broadcaster && !event_sp->BroadcasterIs(m_broadcaster)) + return false; + + if (m_broadcaster_names) + { + bool found_source = false; + const ConstString &event_broadcaster_name = event_sp->GetBroadcaster()->GetBroadcasterName(); + for (uint32_t i=0; i<m_num_broadcaster_names; ++i) + { + if (m_broadcaster_names[i] == event_broadcaster_name) + { + found_source = true; + break; + } + } + if (!found_source) + return false; + } + + if (m_event_type_mask == 0 || m_event_type_mask & event_sp->GetType()) + return true; + return false; + } + +private: + Broadcaster *m_broadcaster; + const ConstString *m_broadcaster_names; + const uint32_t m_num_broadcaster_names; + const uint32_t m_event_type_mask; +}; + + +bool +Listener::FindNextEventInternal +( + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp, + bool remove) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + + Mutex::Locker lock(m_events_mutex); + + if (m_events.empty()) + return false; + + + Listener::event_collection::iterator pos = m_events.end(); + + if (broadcaster == NULL && broadcaster_names == NULL && event_type_mask == 0) + { + pos = m_events.begin(); + } + else + { + pos = std::find_if (m_events.begin(), m_events.end(), EventMatcher (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask)); + } + + if (pos != m_events.end()) + { + event_sp = *pos; + + if (log) + log->Printf ("%p '%s' Listener::FindNextEventInternal(broadcaster=%p, broadcaster_names=%p[%u], event_type_mask=0x%8.8x, remove=%i) event %p", + this, + GetName(), + broadcaster, + broadcaster_names, + num_broadcaster_names, + event_type_mask, + remove, + event_sp.get()); + + if (remove) + { + m_events.erase(pos); + + if (m_events.empty()) + m_cond_wait.SetValue (false, eBroadcastNever); + } + + // Unlock the event queue here. We've removed this event and are about to return + // it so it should be okay to get the next event off the queue here - and it might + // be useful to do that in the "DoOnRemoval". + lock.Unlock(); + + // Don't call DoOnRemoval if you aren't removing the event... + if (remove) + event_sp->DoOnRemoval(); + + return true; + } + + event_sp.reset(); + return false; +} + +Event * +Listener::PeekAtNextEvent () +{ + EventSP event_sp; + if (FindNextEventInternal (NULL, NULL, 0, 0, event_sp, false)) + return event_sp.get(); + return NULL; +} + +Event * +Listener::PeekAtNextEventForBroadcaster (Broadcaster *broadcaster) +{ + EventSP event_sp; + if (FindNextEventInternal (broadcaster, NULL, 0, 0, event_sp, false)) + return event_sp.get(); + return NULL; +} + +Event * +Listener::PeekAtNextEventForBroadcasterWithType (Broadcaster *broadcaster, uint32_t event_type_mask) +{ + EventSP event_sp; + if (FindNextEventInternal (broadcaster, NULL, 0, event_type_mask, event_sp, false)) + return event_sp.get(); + return NULL; +} + + +bool +Listener::GetNextEventInternal +( + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + return FindNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp, true); +} + +bool +Listener::GetNextEvent (EventSP &event_sp) +{ + return GetNextEventInternal (NULL, NULL, 0, 0, event_sp); +} + + +bool +Listener::GetNextEventForBroadcaster (Broadcaster *broadcaster, EventSP &event_sp) +{ + return GetNextEventInternal (broadcaster, NULL, 0, 0, event_sp); +} + +bool +Listener::GetNextEventForBroadcasterWithType (Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp) +{ + return GetNextEventInternal (broadcaster, NULL, 0, event_type_mask, event_sp); +} + + +bool +Listener::WaitForEventsInternal +( + const TimeValue *timeout, + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + bool timed_out = false; + + if (log) + { + log->Printf ("%p Listener::WaitForEventsInternal (timeout = { %p }) for %s", + this, timeout, m_name.c_str()); + } + + while (1) + { + // Note, we don't want to lock the m_events_mutex in the call to GetNextEventInternal, since the DoOnRemoval + // code might require that new events be serviced. For instance, the Breakpoint Command's + if (GetNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp)) + return true; + + { + // Reset condition value to false, so we can wait for new events to be + // added that might meet our current filter + // But first poll for any new event that might satisfy our condition, and if so consume it, + // otherwise wait. + + Mutex::Locker event_locker(m_events_mutex); + const bool remove = false; + if (FindNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp, remove)) + continue; + else + m_cond_wait.SetValue (false, eBroadcastNever); + } + + if (m_cond_wait.WaitForValueEqualTo (true, timeout, &timed_out)) + continue; + + else if (timed_out) + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + log->Printf ("%p Listener::WaitForEventsInternal() timed out for %s", this, m_name.c_str()); + break; + } + else + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + log->Printf ("%p Listener::WaitForEventsInternal() unknown error for %s", this, m_name.c_str()); + break; + } + } + + return false; +} + +bool +Listener::WaitForEventForBroadcasterWithType +( + const TimeValue *timeout, + Broadcaster *broadcaster, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + return WaitForEventsInternal (timeout, broadcaster, NULL, 0, event_type_mask, event_sp); +} + +bool +Listener::WaitForEventForBroadcaster +( + const TimeValue *timeout, + Broadcaster *broadcaster, + EventSP &event_sp +) +{ + return WaitForEventsInternal (timeout, broadcaster, NULL, 0, 0, event_sp); +} + +bool +Listener::WaitForEvent (const TimeValue *timeout, EventSP &event_sp) +{ + return WaitForEventsInternal (timeout, NULL, NULL, 0, 0, event_sp); +} + +//Listener::broadcaster_collection::iterator +//Listener::FindBroadcasterWithMask (Broadcaster *broadcaster, uint32_t event_mask, bool exact) +//{ +// broadcaster_collection::iterator pos; +// broadcaster_collection::iterator end = m_broadcasters.end(); +// for (pos = m_broadcasters.find (broadcaster); +// pos != end && pos->first == broadcaster; +// ++pos) +// { +// if (exact) +// { +// if ((event_mask & pos->second.event_mask) == event_mask) +// return pos; +// } +// else +// { +// if (event_mask & pos->second.event_mask) +// return pos; +// } +// } +// return end; +//} + +size_t +Listener::HandleBroadcastEvent (EventSP &event_sp) +{ + size_t num_handled = 0; + Mutex::Locker locker(m_broadcasters_mutex); + Broadcaster *broadcaster = event_sp->GetBroadcaster(); + broadcaster_collection::iterator pos; + broadcaster_collection::iterator end = m_broadcasters.end(); + for (pos = m_broadcasters.find (broadcaster); + pos != end && pos->first == broadcaster; + ++pos) + { + BroadcasterInfo info = pos->second; + if (event_sp->GetType () & info.event_mask) + { + if (info.callback != NULL) + { + info.callback (event_sp, info.callback_user_data); + ++num_handled; + } + } + } + return num_handled; +} + +uint32_t +Listener::StartListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec) +{ + // The BroadcasterManager mutex must be locked before m_broadcasters_mutex + // to avoid violating the lock hierarchy (manager before broadcasters). + Mutex::Locker manager_locker(manager.m_manager_mutex); + Mutex::Locker locker(m_broadcasters_mutex); + + uint32_t bits_acquired = manager.RegisterListenerForEvents(*this, event_spec); + if (bits_acquired) + m_broadcaster_managers.push_back(&manager); + + return bits_acquired; +} + +bool +Listener::StopListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec) +{ + Mutex::Locker locker(m_broadcasters_mutex); + return manager.UnregisterListenerForEvents (*this, event_spec); + +} + + diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp new file mode 100644 index 0000000..d73ab15 --- /dev/null +++ b/source/Core/Log.cpp @@ -0,0 +1,529 @@ +//===-- Log.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include <pthread.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> + +// C++ Includes +#include <map> +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/Args.h" +using namespace lldb; +using namespace lldb_private; + +Log::Log () : + m_stream_sp(), + m_options(0), + m_mask_bits(0) +{ +} + +Log::Log (const StreamSP &stream_sp) : + m_stream_sp(stream_sp), + m_options(0), + m_mask_bits(0) +{ +} + +Log::~Log () +{ +} + +Flags & +Log::GetOptions() +{ + return m_options; +} + +const Flags & +Log::GetOptions() const +{ + return m_options; +} + +Flags & +Log::GetMask() +{ + return m_mask_bits; +} + +const Flags & +Log::GetMask() const +{ + return m_mask_bits; +} + + +//---------------------------------------------------------------------- +// All logging eventually boils down to this function call. If we have +// a callback registered, then we call the logging callback. If we have +// a valid file handle, we also log to the file. +//---------------------------------------------------------------------- +void +Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) +{ + if (m_stream_sp) + { + static uint32_t g_sequence_id = 0; + StreamString header; + // Enabling the thread safe logging actually deadlocks right now. + // Need to fix this at some point. +// static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); +// Mutex::Locker locker (g_LogThreadedMutex); + + // Add a sequence ID if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_SEQUENCE)) + header.Printf ("%u ", ++g_sequence_id); + + // Timestamp if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) + { + struct timeval tv = TimeValue::Now().GetAsTimeVal(); + header.Printf ("%9ld.%6.6d ", tv.tv_sec, (int32_t)tv.tv_usec); + } + + // Add the process and thread if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD)) + header.Printf ("[%4.4x/%4.4" PRIx64 "]: ", getpid(), Host::GetCurrentThreadID()); + + // Add the process and thread if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) + { + std::string thread_name (Host::GetThreadName (getpid(), Host::GetCurrentThreadID())); + if (!thread_name.empty()) + header.Printf ("%s ", thread_name.c_str()); + } + + header.PrintfVarArg (format, args); + m_stream_sp->Printf("%s\n", header.GetData()); + + if (m_options.Test (LLDB_LOG_OPTION_BACKTRACE)) + Host::Backtrace (*m_stream_sp, 1024); + m_stream_sp->Flush(); + } +} + + +void +Log::PutCString (const char *cstr) +{ + Printf ("%s", cstr); +} + + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::Printf(const char *format, ...) +{ + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (0, format, args); + va_end (args); +} + +void +Log::VAPrintf (const char *format, va_list args) +{ + PrintfWithFlagsVarArg (0, format, args); +} + + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::PrintfWithFlags (uint32_t flags, const char *format, ...) +{ + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (flags, format, args); + va_end (args); +} + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global debug option is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +Log::Debug (const char *format, ...) +{ + if (GetOptions().Test(LLDB_LOG_OPTION_DEBUG)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global debug option is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +Log::DebugVerbose (const char *format, ...) +{ + if (GetOptions().AllSet (LLDB_LOG_OPTION_DEBUG | LLDB_LOG_OPTION_VERBOSE)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG | LLDB_LOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Log only if all of the bits are set +//---------------------------------------------------------------------- +void +Log::LogIf (uint32_t bits, const char *format, ...) +{ + if (m_options.AllSet (bits)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (0, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Printing of errors that are not fatal. +//---------------------------------------------------------------------- +void +Log::Error (const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_ERROR, "error: %s", arg_msg); + free (arg_msg); + } +} + +//---------------------------------------------------------------------- +// Printing of errors that ARE fatal. Exit with ERR exit code +// immediately. +//---------------------------------------------------------------------- +void +Log::FatalError (int err, const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_ERROR | LLDB_LOG_FLAG_FATAL, "error: %s", arg_msg); + ::free (arg_msg); + } + ::exit (err); +} + + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +Log::Verbose (const char *format, ...) +{ + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +Log::WarningVerbose (const char *format, ...) +{ + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_WARNING | LLDB_LOG_FLAG_VERBOSE, "warning: %s", arg_msg); + free (arg_msg); + } + } +} +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal. +//---------------------------------------------------------------------- +void +Log::Warning (const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_WARNING, "warning: %s", arg_msg); + free (arg_msg); + } +} + +typedef std::map <ConstString, Log::Callbacks> CallbackMap; +typedef CallbackMap::iterator CallbackMapIter; + +typedef std::map <ConstString, LogChannelSP> LogChannelMap; +typedef LogChannelMap::iterator LogChannelMapIter; + + +// Surround our callback map with a singleton function so we don't have any +// global initializers. +static CallbackMap & +GetCallbackMap () +{ + static CallbackMap g_callback_map; + return g_callback_map; +} + +static LogChannelMap & +GetChannelMap () +{ + static LogChannelMap g_channel_map; + return g_channel_map; +} + +void +Log::RegisterLogChannel (const ConstString &channel, const Log::Callbacks &log_callbacks) +{ + GetCallbackMap().insert(std::make_pair(channel, log_callbacks)); +} + +bool +Log::UnregisterLogChannel (const ConstString &channel) +{ + return GetCallbackMap().erase(channel) != 0; +} + +bool +Log::GetLogChannelCallbacks (const ConstString &channel, Log::Callbacks &log_callbacks) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos = callback_map.find(channel); + if (pos != callback_map.end()) + { + log_callbacks = pos->second; + return true; + } + ::memset (&log_callbacks, 0, sizeof(log_callbacks)); + return false; +} + +void +Log::EnableAllLogChannels +( + StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm +) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.enable (log_stream_sp, log_options, categories, feedback_strm); + + LogChannelMap &channel_map = GetChannelMap (); + LogChannelMapIter channel_pos, channel_end = channel_map.end(); + for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos) + { + channel_pos->second->Enable (log_stream_sp, log_options, feedback_strm, categories); + } + +} + +void +Log::AutoCompleteChannelName (const char *channel_name, StringList &matches) +{ + LogChannelMap &map = GetChannelMap (); + LogChannelMapIter pos, end = map.end(); + for (pos = map.begin(); pos != end; ++pos) + { + const char *pos_channel_name = pos->first.GetCString(); + if (channel_name && channel_name[0]) + { + if (NameMatches (channel_name, eNameMatchStartsWith, pos_channel_name)) + { + matches.AppendString(pos_channel_name); + } + } + else + matches.AppendString(pos_channel_name); + + } +} + +void +Log::DisableAllLogChannels (Stream *feedback_strm) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + const char *categories[1] = {NULL}; + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.disable (categories, feedback_strm); + + LogChannelMap &channel_map = GetChannelMap (); + LogChannelMapIter channel_pos, channel_end = channel_map.end(); + for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos) + channel_pos->second->Disable (categories, feedback_strm); +} + +void +Log::Initialize() +{ + Log::Callbacks log_callbacks = { DisableLog, EnableLog, ListLogCategories }; + Log::RegisterLogChannel (ConstString("lldb"), log_callbacks); +} + +void +Log::Terminate () +{ + DisableAllLogChannels (NULL); +} + +void +Log::ListAllLogChannels (Stream *strm) +{ + CallbackMap &callback_map = GetCallbackMap (); + LogChannelMap &channel_map = GetChannelMap (); + + if (callback_map.empty() && channel_map.empty()) + { + strm->PutCString ("No logging channels are currently registered.\n"); + return; + } + + CallbackMapIter pos, end = callback_map.end(); + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.list_categories (strm); + + uint32_t idx = 0; + const char *name; + for (idx = 0; (name = PluginManager::GetLogChannelCreateNameAtIndex (idx)) != NULL; ++idx) + { + LogChannelSP log_channel_sp(LogChannel::FindPlugin (name)); + if (log_channel_sp) + log_channel_sp->ListCategories (strm); + } +} + +bool +Log::GetVerbose() const +{ + // FIXME: This has to be centralized between the stream and the log... + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + return true; + + if (m_stream_sp) + return m_stream_sp->GetVerbose(); + return false; +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Log::GetDebug() const +{ + if (m_stream_sp) + return m_stream_sp->GetDebug(); + return false; +} + + +LogChannelSP +LogChannel::FindPlugin (const char *plugin_name) +{ + LogChannelSP log_channel_sp; + LogChannelMap &channel_map = GetChannelMap (); + ConstString log_channel_name (plugin_name); + LogChannelMapIter pos = channel_map.find (log_channel_name); + if (pos == channel_map.end()) + { + ConstString const_plugin_name (plugin_name); + LogChannelCreateInstance create_callback = PluginManager::GetLogChannelCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + log_channel_sp.reset(create_callback()); + if (log_channel_sp) + { + // Cache the one and only loaded instance of each log channel + // plug-in after it has been loaded once. + channel_map[log_channel_name] = log_channel_sp; + } + } + } + else + { + // We have already loaded an instance of this log channel class, + // so just return the cached instance. + log_channel_sp = pos->second; + } + return log_channel_sp; +} + +LogChannel::LogChannel () : + m_log_ap () +{ +} + +LogChannel::~LogChannel () +{ +} + + diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp new file mode 100644 index 0000000..4655eb1 --- /dev/null +++ b/source/Core/Mangled.cpp @@ -0,0 +1,313 @@ +//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// FreeBSD9-STABLE requires this to know about size_t in cxxabi.h +#include <cstddef> +#include <cxxabi.h> + + +#include "llvm/ADT/DenseMap.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +using namespace lldb_private; + +static inline bool +cstring_is_mangled (const char *s) +{ + if (s) + return s[0] == '_' && s[1] == 'Z'; + return false; +} + +#pragma mark Mangled +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Mangled::Mangled () : + m_mangled(), + m_demangled() +{ +} + +//---------------------------------------------------------------------- +// Constructor with an optional string and a boolean indicating if it is +// the mangled version. +//---------------------------------------------------------------------- +Mangled::Mangled (const ConstString &s, bool mangled) : + m_mangled(), + m_demangled() +{ + if (s) + SetValue(s, mangled); +} + +Mangled::Mangled (const ConstString &s) : + m_mangled(), + m_demangled() +{ + if (s) + SetValue(s); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Mangled::~Mangled () +{ +} + +//---------------------------------------------------------------------- +// 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) : NULL; +} + +//---------------------------------------------------------------------- +// 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 the string values. +//---------------------------------------------------------------------- +int +Mangled::Compare (const Mangled& a, const Mangled& b) +{ + return ConstString::Compare(a.GetName(ePreferMangled), a.GetName(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 (const 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 (const 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(); + } +} + + +//---------------------------------------------------------------------- +// 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. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetDemangledName () 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) + { + // We need to generate and cache the demangled name. + Timer scoped_timer (__PRETTY_FUNCTION__, + "Mangled::GetDemangledName (m_mangled = %s)", + m_mangled.GetCString()); + + // Don't bother running anything that isn't mangled + const char *mangled_cstr = m_mangled.GetCString(); + if (cstring_is_mangled(mangled_cstr)) + { + if (!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 = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL); + + if (demangled_name) + { + m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); + free (demangled_name); + } + } + } + if (!m_demangled) + { + // Set the demangled string to the empty string to indicate we + // tried to parse it once and failed. + m_demangled.SetCString(""); + } + } + + return m_demangled; +} + + +bool +Mangled::NameMatches (const RegularExpression& regex) const +{ + if (m_mangled && regex.Execute (m_mangled.AsCString())) + return true; + + if (GetDemangledName() && regex.Execute (m_demangled.AsCString())) + return true; + return false; +} + +//---------------------------------------------------------------------- +// Get the demangled name if there is one, else return the mangled name. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetName (Mangled::NamePreference preference) const +{ + if (preference == ePreferDemangled) + { + // Call the accessor to make sure we get a demangled name in case + // it hasn't been demangled yet... + if (GetDemangledName()) + return m_demangled; + return m_mangled; + } + else + { + if (m_mangled) + return m_mangled; + return GetDemangledName(); + } +} + +//---------------------------------------------------------------------- +// 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 = ", (int)sizeof(void*) * 2, 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(); +} + +//---------------------------------------------------------------------- +// Dump OBJ to the supplied stream S. +//---------------------------------------------------------------------- +Stream& +operator << (Stream& s, const Mangled& obj) +{ + if (obj.GetMangledName()) + s << "mangled = '" << obj.GetMangledName() << "'"; + + const ConstString& demangled = obj.GetDemangledName(); + if (demangled) + s << ", demangled = '" << demangled << '\''; + else + s << ", demangled = <error>"; + return s; +} diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp new file mode 100644 index 0000000..4252ed4 --- /dev/null +++ b/source/Core/Module.cpp @@ -0,0 +1,1609 @@ +//===-- Module.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolFile.h" + +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 = NULL; + if (g_module_collection == NULL) + g_module_collection = new ModuleCollection(); + + return *g_module_collection; +} + +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 Mutex *g_module_collection_mutex = NULL; + if (g_module_collection_mutex == NULL) + g_module_collection_mutex = new Mutex (Mutex::eMutexTypeRecursive); // NOTE: known leak + return g_module_collection_mutex; +} + +size_t +Module::GetNumberAllocatedModules () +{ + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + return GetModuleCollection().size(); +} + +Module * +Module::GetAllocatedModuleAtIndex (size_t idx) +{ + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + if (idx < modules.size()) + return modules[idx]; + return NULL; +} +#if 0 + +// These functions help us to determine if modules are still loaded, yet don't require that +// you have a command interpreter and can easily be called from an external debugger. +namespace lldb { + + void + ClearModuleInfo (void) + { + const bool mandatory = true; + ModuleList::RemoveOrphanSharedModules(mandatory); + } + + void + DumpModuleInfo (void) + { + Mutex::Locker locker (Module::GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + const size_t count = modules.size(); + printf ("%s: %" PRIu64 " modules:\n", __PRETTY_FUNCTION__, (uint64_t)count); + for (size_t i=0; i<count; ++i) + { + + StreamString strm; + Module *module = modules[i]; + const bool in_shared_module_list = ModuleList::ModuleIsInCache (module); + module->GetDescription(&strm, eDescriptionLevelFull); + printf ("%p: shared = %i, ref_count = %3u, module = %s\n", + module, + in_shared_module_list, + (uint32_t)module->use_count(), + strm.GetString().c_str()); + } + } +} + +#endif + +Module::Module (const ModuleSpec &module_spec) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (module_spec.GetFileSpec().GetModificationTime()), + m_arch (module_spec.GetArchitecture()), + m_uuid (), + m_file (module_spec.GetFileSpec()), + m_platform_file(module_spec.GetPlatformFileSpec()), + m_symfile_spec (module_spec.GetSymbolFileSpec()), + m_object_name (module_spec.GetObjectName()), + m_object_offset (module_spec.GetObjectOffset()), + m_object_mod_time (module_spec.GetObjectModificationTime()), + m_objfile_sp (), + m_symfile_ap (), + m_ast (), + m_source_mappings (), + m_did_load_objfile (false), + m_did_load_symbol_vendor (false), + m_did_parse_uuid (false), + m_did_init_ast (false), + m_is_dynamic_loader_module (false), + m_file_has_changed (false), + m_first_file_changed_log (false) +{ + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + GetModuleCollection().push_back(this); + } + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::Module((%s) '%s%s%s%s')", + 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(const FileSpec& file_spec, + const ArchSpec& arch, + const ConstString *object_name, + off_t object_offset, + const TimeValue *object_mod_time_ptr) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (file_spec.GetModificationTime()), + m_arch (arch), + m_uuid (), + m_file (file_spec), + m_platform_file(), + m_symfile_spec (), + m_object_name (), + m_object_offset (object_offset), + m_object_mod_time (), + m_objfile_sp (), + m_symfile_ap (), + m_ast (), + m_source_mappings (), + m_did_load_objfile (false), + m_did_load_symbol_vendor (false), + m_did_parse_uuid (false), + m_did_init_ast (false), + m_is_dynamic_loader_module (false), + m_file_has_changed (false), + m_first_file_changed_log (false) +{ + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + GetModuleCollection().push_back(this); + } + + if (object_name) + m_object_name = *object_name; + + if (object_mod_time_ptr) + m_object_mod_time = *object_mod_time_ptr; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::Module((%s) '%s%s%s%s')", + 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() +{ + // 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 + Mutex::Locker locker (m_mutex); + // Scope for locker below... + { + Mutex::Locker locker (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) + log->Printf ("%p Module::~Module((%s) '%s%s%s%s')", + 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_ap.reset(); + m_symfile_ap.reset(); + m_objfile_sp.reset(); +} + +ObjectFile * +Module::GetMemoryObjectFile (const lldb::ProcessSP &process_sp, lldb::addr_t header_addr, Error &error) +{ + if (m_objfile_sp) + { + error.SetErrorString ("object file already exists"); + } + else + { + Mutex::Locker locker (m_mutex); + if (process_sp) + { + m_did_load_objfile = true; + std::unique_ptr<DataBufferHeap> data_ap (new DataBufferHeap (512, 0)); + Error readmem_error; + const size_t bytes_read = process_sp->ReadMemory (header_addr, + data_ap->GetBytes(), + data_ap->GetByteSize(), + readmem_error); + if (bytes_read == 512) + { + DataBufferSP data_sp(data_ap.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.SetCString (s.GetData()); + + // 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_objfile_sp->GetArchitecture (m_arch); + } + 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() +{ + Mutex::Locker locker (m_mutex); + if (m_did_parse_uuid == false) + { + ObjectFile * obj_file = GetObjectFile (); + + if (obj_file != NULL) + { + obj_file->GetUUID(&m_uuid); + m_did_parse_uuid = true; + } + } + return m_uuid; +} + +ClangASTContext & +Module::GetClangASTContext () +{ + Mutex::Locker locker (m_mutex); + if (m_did_init_ast == false) + { + ObjectFile * objfile = GetObjectFile(); + ArchSpec object_arch; + if (objfile && objfile->GetArchitecture(object_arch)) + { + m_did_init_ast = true; + + // LLVM wants this to be set to iOS or MacOSX; if we're working on + // a bare-boards type image, change the triple for llvm's benefit. + if (object_arch.GetTriple().getVendor() == llvm::Triple::Apple + && object_arch.GetTriple().getOS() == llvm::Triple::UnknownOS) + { + if (object_arch.GetTriple().getArch() == llvm::Triple::arm || + object_arch.GetTriple().getArch() == llvm::Triple::thumb) + { + object_arch.GetTriple().setOS(llvm::Triple::IOS); + } + else + { + object_arch.GetTriple().setOS(llvm::Triple::MacOSX); + } + } + m_ast.SetArchitecture (object_arch); + } + } + return m_ast; +} + +void +Module::ParseAllDebugSymbols() +{ + Mutex::Locker locker (m_mutex); + size_t num_comp_units = GetNumCompileUnits(); + if (num_comp_units == 0) + return; + + SymbolContext sc; + sc.module_sp = shared_from_this(); + SymbolVendor *symbols = GetSymbolVendor (); + + 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) + { + sc.function = NULL; + symbols->ParseVariablesForContext(sc); + + symbols->ParseCompileUnitFunctions(sc); + + for (size_t func_idx = 0; (sc.function = sc.comp_unit->GetFunctionAtIndex(func_idx).get()) != NULL; ++func_idx) + { + symbols->ParseFunctionBlocks(sc); + + // Parse the variables for this function and all its blocks + symbols->ParseVariablesForContext(sc); + } + + + // Parse all types for this compile unit + sc.function = NULL; + symbols->ParseTypes(sc); + } + } +} + +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}", this); +} + +size_t +Module::GetNumCompileUnits() +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, "Module::GetNumCompileUnits (module = %p)", this); + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->GetNumCompileUnits(); + return 0; +} + +CompUnitSP +Module::GetCompileUnitAtIndex (size_t index) +{ + Mutex::Locker locker (m_mutex); + size_t num_comp_units = GetNumCompileUnits (); + CompUnitSP cu_sp; + + if (index < num_comp_units) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + cu_sp = symbols->GetCompileUnitAtIndex(index); + } + return cu_sp; +} + +bool +Module::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, "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, uint32_t resolve_scope, SymbolContext& sc) +{ + Mutex::Locker locker (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; + + // 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 ) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + resolved_flags |= symbols->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)) + { + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + if (so_addr.IsSectionOffset()) + { + sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + } + } + } + } + return resolved_flags; +} + +uint32_t +Module::ResolveSymbolContextForFilePath +( + const char *file_path, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list +) +{ + FileSpec file_spec(file_path, false); + 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, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, + "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(); + + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + symbols->ResolveSymbolContext (file_spec, line, check_inlines, resolve_scope, sc_list); + + return sc_list.GetSize() - initial_count; +} + + +size_t +Module::FindGlobalVariables (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(name, namespace_decl, append, max_matches, variables); + return 0; +} + +size_t +Module::FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(regex, append, max_matches, variables); + return 0; +} + +size_t +Module::FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list) +{ + if (!append) + sc_list.Clear(); + + const size_t start_size = sc_list.GetSize(); + const size_t num_compile_units = GetNumCompileUnits(); + SymbolContext sc; + sc.module_sp = shared_from_this(); + const bool compare_directory = 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); + } + } + return sc_list.GetSize() - start_size; +} + +size_t +Module::FindFunctions (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + uint32_t name_type_mask, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList& sc_list) +{ + if (!append) + sc_list.Clear(); + + const size_t old_size = sc_list.GetSize(); + + // Find all the functions (not symbols, but debug information functions... + SymbolVendor *symbols = GetSymbolVendor (); + + if (name_type_mask & eFunctionNameTypeAuto) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, + name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + if (symbols) + { + symbols->FindFunctions(lookup_name, + namespace_decl, + lookup_name_type_mask, + include_inlines, + append, + 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_name, lookup_name_type_mask, sc_list); + } + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (i<sc_list.GetSize()) + { + if (sc_list.GetContextAtIndex(i, sc)) + { + const char *func_name = sc.GetFunctionName().GetCString(); + if (func_name && strstr (func_name, name.GetCString()) == NULL) + { + // Remove the current context + sc_list.RemoveContextAtIndex(i); + // Don't increment i and continue in the loop + continue; + } + } + ++i; + } + } + } + else + { + if (symbols) + { + symbols->FindFunctions(name, namespace_decl, name_type_mask, include_inlines, append, 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); + } + } + } + + return sc_list.GetSize() - old_size; +} + +size_t +Module::FindFunctions (const RegularExpression& regex, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList& sc_list) +{ + if (!append) + sc_list.Clear(); + + const size_t start_size = sc_list.GetSize(); + + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + { + symbols->FindFunctions(regex, include_inlines, append, 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 && (sym_type == eSymbolTypeCode || + sym_type == eSymbolTypeResolver)) + { + FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find(sc.symbol->GetAddress().GetFileAddress()); + if (pos == end) + sc_list.Append(sc); + else + sc_list[pos->second].symbol = sc.symbol; + } + } + } + } + } + } + } + return sc_list.GetSize() - start_size; +} + +size_t +Module::FindTypes_Impl (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + if (sc.module_sp.get() == NULL || sc.module_sp.get() == this) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindTypes(sc, name, namespace_decl, append, max_matches, types); + } + return 0; +} + +size_t +Module::FindTypesInNamespace (const SymbolContext& sc, + const ConstString &type_name, + const ClangNamespaceDecl *namespace_decl, + size_t max_matches, + TypeList& type_list) +{ + const bool append = true; + return FindTypes_Impl(sc, type_name, namespace_decl, append, max_matches, type_list); +} + +lldb::TypeSP +Module::FindFirstType (const SymbolContext& sc, + const ConstString &name, + bool exact_match) +{ + TypeList type_list; + const size_t num_matches = FindTypes (sc, name, exact_match, 1, type_list); + if (num_matches) + return type_list.GetTypeAtIndex(0); + return TypeSP(); +} + + +size_t +Module::FindTypes (const SymbolContext& sc, + const ConstString &name, + bool exact_match, + size_t max_matches, + TypeList& types) +{ + size_t num_matches = 0; + const char *type_name_cstr = name.GetCString(); + std::string type_scope; + std::string type_basename; + const bool append = true; + TypeClass type_class = eTypeClassAny; + 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 qualfied names to match + + if (type_scope.size() >= 2 && type_scope[0] == ':' && type_scope[1] == ':') + { + type_scope.erase(0,2); + exact_match = true; + } + ConstString type_basename_const_str (type_basename.c_str()); + if (FindTypes_Impl(sc, type_basename_const_str, NULL, append, max_matches, types)) + { + types.RemoveMismatchedTypes (type_scope, type_basename, type_class, exact_match); + num_matches = types.GetSize(); + } + } + else + { + // The type is not in a namespace/class scope, just search for it by basename + if (type_class != eTypeClassAny) + { + // The "type_name_cstr" will have been modified if we have a valid type class + // prefix (like "struct", "class", "union", "typedef" etc). + num_matches = FindTypes_Impl(sc, ConstString(type_name_cstr), NULL, append, max_matches, types); + types.RemoveMismatchedTypes (type_class); + num_matches = types.GetSize(); + } + else + { + num_matches = FindTypes_Impl(sc, name, NULL, append, max_matches, types); + } + } + + return num_matches; + +} + +SymbolVendor* +Module::GetSymbolVendor (bool can_create, lldb_private::Stream *feedback_strm) +{ + Mutex::Locker locker (m_mutex); + if (m_did_load_symbol_vendor == false && can_create) + { + ObjectFile *obj_file = GetObjectFile (); + if (obj_file != NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + m_symfile_ap.reset(SymbolVendor::FindPlugin(shared_from_this(), feedback_strm)); + m_did_load_symbol_vendor = true; + } + } + return m_symfile_ap.get(); +} + +void +Module::SetFileSpecAndObjectName (const FileSpec &file, const 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 = file.GetModificationTime(); + 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) +{ + Mutex::Locker locker (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.GetString().c_str()); + + } +} + +bool +Module::FileHasChanged () const +{ + if (m_file_has_changed == false) + m_file_has_changed = (m_file.GetModificationTime() != m_mod_time); + return m_file_has_changed; +} + +void +Module::ReportErrorIfModifyDetected (const char *format, ...) +{ + if (m_first_file_changed_log == false) + { + 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.GetString().c_str()); + } + } + } +} + +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.GetString().c_str()); + } +} + +void +Module::LogMessage (Log *log, const char *format, ...) +{ + if (log) + { + 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.GetString().c_str()); + } +} + +void +Module::LogMessageVerboseBacktrace (Log *log, const char *format, ...) +{ + if (log) + { + 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()) + Host::Backtrace (log_message, 1024); + log->PutCString(log_message.GetString().c_str()); + } +} + +void +Module::Dump(Stream *s) +{ + Mutex::Locker locker (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); + + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + symbols->Dump(s); + + s->IndentLess(); +} + + +TypeList* +Module::GetTypeList () +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return &symbols->GetTypeList(); + return NULL; +} + +const ConstString & +Module::GetObjectName() const +{ + return m_object_name; +} + +ObjectFile * +Module::GetObjectFile() +{ + Mutex::Locker locker (m_mutex); + if (m_did_load_objfile == false) + { + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); + DataBufferSP data_sp; + lldb::offset_t data_offset = 0; + const lldb::offset_t file_size = m_file.GetByteSize(); + 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. + m_objfile_sp->GetArchitecture (m_arch); + } + } + } + return m_objfile_sp.get(); +} + +SectionList * +Module::GetSectionList() +{ + // Populate m_unified_sections_ap with sections from objfile. + if (m_sections_ap.get() == NULL) + { + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + obj_file->CreateSections(*GetUnifiedSectionList()); + } + return m_sections_ap.get(); +} + +SectionList * +Module::GetUnifiedSectionList() +{ + // Populate m_unified_sections_ap with sections from objfile. + if (m_sections_ap.get() == NULL) + m_sections_ap.reset(new SectionList()); + return m_sections_ap.get(); +} + +const Symbol * +Module::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindFirstSymbolWithNameAndType (name = %s, type = %i)", + name.AsCString(), + symbol_type); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + return symtab->FindFirstSymbolWithNameAndType (name, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny); + } + return NULL; +} +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); + } + } +} + +size_t +Module::FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsFunctions (name = %s, mask = 0x%8.8x)", + name.AsCString(), + name_type_mask); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + return symtab->FindFunctionSymbols (name, name_type_mask, sc_list); + } + return 0; +} + +size_t +Module::FindSymbolsWithNameAndType (const 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. + + + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsWithNameAndType (name = %s, type = %i)", + name.AsCString(), + symbol_type); + const size_t initial_size = sc_list.GetSize(); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + std::vector<uint32_t> symbol_indexes; + symtab->FindAllSymbolsWithNameAndType (name, symbol_type, symbol_indexes); + SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); + } + } + return sc_list.GetSize() - initial_size; +} + +size_t +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. + + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)", + regex.GetText(), + symbol_type); + const size_t initial_size = sc_list.GetSize(); + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + std::vector<uint32_t> symbol_indexes; + symtab->FindAllSymbolsMatchingRexExAndType (regex, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); + SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); + } + } + return sc_list.GetSize() - initial_size; +} + +void +Module::SetSymbolFileFileSpec (const FileSpec &file) +{ + // Remove any sections in the unified section list that come from the current symbol vendor. + if (m_symfile_ap) + { + SectionList *section_list = GetSectionList(); + SymbolFile *symbol_file = m_symfile_ap->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 && 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); + } + } + } + } + } + + m_symfile_spec = file; + m_symfile_ap.reset(); + m_did_load_symbol_vendor = false; +} + +bool +Module::IsExecutable () +{ + if (GetObjectFile() == NULL) + return false; + else + return GetObjectFile()->IsExecutable(); +} + +bool +Module::IsLoadedInTarget (Target *target) +{ + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + { + SectionList *sections = GetSectionList(); + if (sections != NULL) + { + 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, Error& error, Stream* feedback_stream) +{ + if (!target) + { + error.SetErrorString("invalid destination Target"); + return false; + } + + LoadScriptFromSymFile shoud_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); + + 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); + + + const uint32_t num_specs = file_specs.GetSize(); + if (num_specs) + { + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + if (script_interpreter) + { + for (uint32_t i=0; i<num_specs; ++i) + { + FileSpec scripting_fspec (file_specs.GetFileSpecAtIndex(i)); + if (scripting_fspec && scripting_fspec.Exists()) + { + if (shoud_load == eLoadScriptFromSymFileFalse) + return false; + if (shoud_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.IsExactMatch(new_arch); +} + +bool +Module::SetLoadAddress (Target &target, lldb::addr_t offset, bool &changed) +{ + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + // Only load non-thread specific sections when given a slide + if (section_sp && !section_sp->IsThreadSpecific()) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + offset)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + return num_loaded_sections > 0; +} + + +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... + if (uuid == GetUUID()) + return true; + else + return false; + } + + const FileSpec &file_spec = module_ref.GetFileSpec(); + if (file_spec) + { + if (!FileSpec::Equal (file_spec, m_file, file_spec.GetDirectory())) + return false; + } + + const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec(); + if (platform_file_spec) + { + if (!FileSpec::Equal (platform_file_spec, GetPlatformFileSpec (), platform_file_spec.GetDirectory())) + return false; + } + + const ArchSpec &arch = module_ref.GetArchitecture(); + if (arch.IsValid()) + { + if (!m_arch.IsCompatibleMatch(arch)) + return false; + } + + const 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 +{ + Mutex::Locker locker (m_mutex); + return m_source_mappings.FindFile (orig_spec, new_spec); +} + +bool +Module::RemapSourceFile (const char *path, std::string &new_path) const +{ + Mutex::Locker locker (m_mutex); + return m_source_mappings.RemapPath(path, new_path); +} + +uint32_t +Module::GetVersion (uint32_t *versions, uint32_t num_versions) +{ + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + return obj_file->GetVersion (versions, num_versions); + + if (versions && num_versions) + { + for (uint32_t i=0; i<num_versions; ++i) + versions[i] = UINT32_MAX; + } + return 0; +} + +void +Module::PrepareForFunctionNameLookup (const ConstString &name, + uint32_t name_type_mask, + ConstString &lookup_name, + uint32_t &lookup_name_type_mask, + bool &match_name_after_lookup) +{ + const char *name_cstr = name.GetCString(); + lookup_name_type_mask = eFunctionNameTypeNone; + match_name_after_lookup = false; + const char *base_name_start = NULL; + const char *base_name_end = NULL; + + if (name_type_mask & eFunctionNameTypeAuto) + { + if (CPPLanguageRuntime::IsCPPMangledName (name_cstr)) + lookup_name_type_mask = eFunctionNameTypeFull; + else if (ObjCLanguageRuntime::IsPossibleObjCMethodName (name_cstr)) + lookup_name_type_mask = eFunctionNameTypeFull; + else + { + if (ObjCLanguageRuntime::IsPossibleObjCSelector(name_cstr)) + lookup_name_type_mask |= eFunctionNameTypeSelector; + + CPPLanguageRuntime::MethodName cpp_method (name); + llvm::StringRef basename (cpp_method.GetBasename()); + if (basename.empty()) + { + if (CPPLanguageRuntime::StripNamespacesFromVariableName (name_cstr, base_name_start, base_name_end)) + lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); + } + else + { + base_name_start = basename.data(); + base_name_end = base_name_start + basename.size(); + lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); + } + } + } + else + { + lookup_name_type_mask = name_type_mask; + if (lookup_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. + CPPLanguageRuntime::MethodName cpp_method (name); + if (cpp_method.IsValid()) + { + llvm::StringRef basename (cpp_method.GetBasename()); + base_name_start = basename.data(); + base_name_end = base_name_start + basename.size(); + + if (!cpp_method.GetQualifiers().empty()) + { + // There is a "const" or other qualifer following the end of the fucntion parens, + // this can't be a eFunctionNameTypeBase + lookup_name_type_mask &= ~(eFunctionNameTypeBase); + if (lookup_name_type_mask == eFunctionNameTypeNone) + return; + } + } + else + { + if (!CPPLanguageRuntime::StripNamespacesFromVariableName (name_cstr, base_name_start, base_name_end)) + { + lookup_name_type_mask &= ~(eFunctionNameTypeMethod | eFunctionNameTypeBase); + if (lookup_name_type_mask == eFunctionNameTypeNone) + return; + } + } + } + + if (lookup_name_type_mask & eFunctionNameTypeSelector) + { + if (!ObjCLanguageRuntime::IsPossibleObjCSelector(name_cstr)) + { + lookup_name_type_mask &= ~(eFunctionNameTypeSelector); + if (lookup_name_type_mask == eFunctionNameTypeNone) + return; + } + } + } + + if (base_name_start && + base_name_end && + base_name_start != name_cstr && + base_name_start < base_name_end) + { + // 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 + lookup_name.SetCStringWithLength(base_name_start, base_name_end - base_name_start); + 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" + lookup_name = name; + match_name_after_lookup = false; + } +}
\ No newline at end of file diff --git a/source/Core/ModuleChild.cpp b/source/Core/ModuleChild.cpp new file mode 100644 index 0000000..9637fc3 --- /dev/null +++ b/source/Core/ModuleChild.cpp @@ -0,0 +1,46 @@ +//===-- ModuleChild.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ModuleChild.h" + +using namespace lldb_private; + +ModuleChild::ModuleChild (const lldb::ModuleSP &module_sp) : + m_module_wp (module_sp) +{ +} + +ModuleChild::ModuleChild (const ModuleChild& rhs) : + m_module_wp(rhs.m_module_wp) +{ +} + +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/source/Core/ModuleList.cpp b/source/Core/ModuleList.cpp new file mode 100644 index 0000000..ebc6702 --- /dev/null +++ b/source/Core/ModuleList.cpp @@ -0,0 +1,1103 @@ +//===-- ModuleList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ModuleList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/VariableList.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ModuleList constructor +//---------------------------------------------------------------------- +ModuleList::ModuleList() : + m_modules(), + m_modules_mutex (Mutex::eMutexTypeRecursive), + m_notifier(NULL) +{ +} + +//---------------------------------------------------------------------- +// Copy constructor +//---------------------------------------------------------------------- +ModuleList::ModuleList(const ModuleList& rhs) : + m_modules(), + m_modules_mutex (Mutex::eMutexTypeRecursive) +{ + Mutex::Locker lhs_locker(m_modules_mutex); + Mutex::Locker rhs_locker(rhs.m_modules_mutex); + m_modules = rhs.m_modules; +} + +ModuleList::ModuleList (ModuleList::Notifier* notifier) : + m_modules(), + m_modules_mutex (Mutex::eMutexTypeRecursive), + m_notifier(notifier) +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const ModuleList& +ModuleList::operator= (const ModuleList& rhs) +{ + if (this != &rhs) + { + Mutex::Locker lhs_locker(m_modules_mutex); + Mutex::Locker rhs_locker(rhs.m_modules_mutex); + m_modules = rhs.m_modules; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ModuleList::~ModuleList() +{ +} + +void +ModuleList::AppendImpl (const ModuleSP &module_sp, bool use_notifier) +{ + if (module_sp) + { + Mutex::Locker locker(m_modules_mutex); + m_modules.push_back(module_sp); + if (use_notifier && m_notifier) + m_notifier->ModuleAdded(*this, module_sp); + } +} + +void +ModuleList::Append (const ModuleSP &module_sp) +{ + AppendImpl (module_sp); +} + +void +ModuleList::ReplaceEquivalent (const ModuleSP &module_sp) +{ + if (module_sp) + { + Mutex::Locker locker(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) +{ + if (module_sp) + { + Mutex::Locker locker(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); + 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) + { + Mutex::Locker locker(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->ModuleRemoved(*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->ModuleRemoved(*this, module_sp); + return retval; +} + +bool +ModuleList::Remove (const ModuleSP &module_sp) +{ + return RemoveImpl (module_sp); +} + +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->ModuleUpdated(*this, old_module_sp,new_module_sp); + return true; +} + +bool +ModuleList::RemoveIfOrphaned (const Module *module_ptr) +{ + if (module_ptr) + { + Mutex::Locker locker(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) +{ + Mutex::Locker locker; + + if (mandatory) + { + locker.Lock (m_modules_mutex); + } + else + { + // Not mandatory, remove orphans if we can get the mutex + if (!locker.TryLock(m_modules_mutex)) + 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) +{ + Mutex::Locker locker(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)) + ++num_removed; + } + return num_removed; +} + + +void +ModuleList::Clear() +{ + ClearImpl(); +} + +void +ModuleList::Destroy() +{ + ClearImpl(); +} + +void +ModuleList::ClearImpl (bool use_notifier) +{ + Mutex::Locker locker(m_modules_mutex); + if (use_notifier && m_notifier) + m_notifier->WillClearList(*this); + m_modules.clear(); +} + +Module* +ModuleList::GetModulePointerAtIndex (size_t idx) const +{ + Mutex::Locker locker(m_modules_mutex); + return GetModulePointerAtIndexUnlocked(idx); +} + +Module* +ModuleList::GetModulePointerAtIndexUnlocked (size_t idx) const +{ + if (idx < m_modules.size()) + return m_modules[idx].get(); + return NULL; +} + +ModuleSP +ModuleList::GetModuleAtIndex(size_t idx) const +{ + Mutex::Locker locker(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; +} + +size_t +ModuleList::FindFunctions (const ConstString &name, + uint32_t name_type_mask, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList &sc_list) const +{ + if (!append) + sc_list.Clear(); + + const size_t old_size = sc_list.GetSize(); + + if (name_type_mask & eFunctionNameTypeAuto) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctions (lookup_name, + NULL, + lookup_name_type_mask, + include_symbols, + include_inlines, + true, + sc_list); + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (i<sc_list.GetSize()) + { + if (sc_list.GetContextAtIndex(i, sc)) + { + const char *func_name = sc.GetFunctionName().GetCString(); + if (func_name && strstr (func_name, name.GetCString()) == NULL) + { + // Remove the current context + sc_list.RemoveContextAtIndex(i); + // Don't increment i and continue in the loop + continue; + } + } + ++i; + } + } + + } + else + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctions (name, NULL, name_type_mask, include_symbols, include_inlines, true, sc_list); + } + } + return sc_list.GetSize() - old_size; +} + +size_t +ModuleList::FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + const size_t old_size = sc_list.GetSize(); + + if (name_type_mask & eFunctionNameTypeAuto) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctionSymbols (lookup_name, + lookup_name_type_mask, + sc_list); + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (i<sc_list.GetSize()) + { + if (sc_list.GetContextAtIndex(i, sc)) + { + const char *func_name = sc.GetFunctionName().GetCString(); + if (func_name && strstr (func_name, name.GetCString()) == NULL) + { + // Remove the current context + sc_list.RemoveContextAtIndex(i); + // Don't increment i and continue in the loop + continue; + } + } + ++i; + } + } + + } + else + { + Mutex::Locker locker(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); + } + } + + return sc_list.GetSize() - old_size; +} + +size_t +ModuleList::FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list) const +{ + if (!append) + sc_list.Clear(); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindCompileUnits (path, true, sc_list); + } + + return sc_list.GetSize(); +} + +size_t +ModuleList::FindGlobalVariables (const ConstString &name, + bool append, + size_t max_matches, + VariableList& variable_list) const +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (name, NULL, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +size_t +ModuleList::FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list) const +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (regex, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +size_t +ModuleList::FindSymbolsWithNameAndType (const ConstString &name, + SymbolType symbol_type, + SymbolContextList &sc_list, + bool append) const +{ + Mutex::Locker locker(m_modules_mutex); + if (!append) + sc_list.Clear(); + size_t initial_size = sc_list.GetSize(); + + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + (*pos)->FindSymbolsWithNameAndType (name, symbol_type, sc_list); + return sc_list.GetSize() - initial_size; +} + +size_t +ModuleList::FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append) const +{ + Mutex::Locker locker(m_modules_mutex); + if (!append) + sc_list.Clear(); + size_t initial_size = sc_list.GetSize(); + + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + (*pos)->FindSymbolsMatchingRegExAndType (regex, symbol_type, sc_list); + return sc_list.GetSize() - initial_size; +} + +size_t +ModuleList::FindModules (const ModuleSpec &module_spec, ModuleList& matching_module_list) const +{ + size_t existing_matches = matching_module_list.GetSize(); + + Mutex::Locker locker(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); + } + return matching_module_list.GetSize() - existing_matches; +} + +ModuleSP +ModuleList::FindModule (const Module *module_ptr) const +{ + ModuleSP module_sp; + + // Scope for "locker" + { + Mutex::Locker locker(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()) + { + Mutex::Locker locker(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; +} + + +size_t +ModuleList::FindTypes (const SymbolContext& sc, const ConstString &name, bool name_is_fully_qualified, size_t max_matches, TypeList& types) const +{ + Mutex::Locker locker(m_modules_mutex); + + size_t total_matches = 0; + collection::const_iterator pos, end = m_modules.end(); + if (sc.module_sp) + { + // The symbol context "sc" contains a module so we want to search that + // one first if it is in our list... + for (pos = m_modules.begin(); pos != end; ++pos) + { + if (sc.module_sp.get() == (*pos).get()) + { + total_matches += (*pos)->FindTypes (sc, name, name_is_fully_qualified, max_matches, types); + + if (total_matches >= max_matches) + break; + } + } + } + + if (total_matches < max_matches) + { + SymbolContext world_sc; + 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 comparisong will always be true (valid_module_ptr != NULL). + if (sc.module_sp.get() != (*pos).get()) + total_matches += (*pos)->FindTypes (world_sc, name, name_is_fully_qualified, max_matches, types); + + if (total_matches >= max_matches) + break; + } + } + + return total_matches; +} + +bool +ModuleList::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const +{ + Mutex::Locker locker(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; +} + + + +ModuleSP +ModuleList::FindFirstModule (const ModuleSpec &module_spec) const +{ + ModuleSP module_sp; + Mutex::Locker locker(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; + { + Mutex::Locker locker(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"; + + Mutex::Locker locker(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) + { + Mutex::Locker locker(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(); + log->Printf ("%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 +{ + Mutex::Locker locker(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, uint32_t 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 + { + Mutex::Locker locker(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, + uint32_t resolve_scope, + SymbolContextList& sc_list +) const +{ + FileSpec file_spec(file_path, false); + 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, uint32_t resolve_scope, SymbolContextList& sc_list) const +{ + Mutex::Locker locker(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) + { + Mutex::Locker locker(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; +} + +static ModuleList & +GetSharedModuleList () +{ + // 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. + static ModuleList *g_shared_module_list = NULL; + if (g_shared_module_list == NULL) + g_shared_module_list = new ModuleList(); // <--- Intentional leak!!! + + return *g_shared_module_list; +} + +bool +ModuleList::ModuleIsInCache (const Module *module_ptr) +{ + if (module_ptr) + { + ModuleList &shared_module_list = GetSharedModuleList (); + return shared_module_list.FindModule (module_ptr).get() != NULL; + } + return false; +} + +size_t +ModuleList::FindSharedModules (const ModuleSpec &module_spec, ModuleList &matching_module_list) +{ + return GetSharedModuleList ().FindModules (module_spec, matching_module_list); +} + +size_t +ModuleList::RemoveOrphanSharedModules (bool mandatory) +{ + return GetSharedModuleList ().RemoveOrphans(mandatory); +} + +Error +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 (); + Mutex::Locker locker(shared_module_list.m_modules_mutex); + char path[PATH_MAX]; + + Error 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 == false) + { + ModuleList matching_module_list; + const size_t num_matching_modules = shared_module_list.FindModules (module_spec, matching_module_list); + 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->get()) + *old_module_sp_ptr = module_sp; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_MODULES)); + if (log) + log->Printf("module changed: %p, removing from global module list", 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; + else + { + module_sp.reset (new 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) + { + 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 (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + return error; + } + } + 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. + FileSpec file_spec = Symbols::LocateExecutableObjectFile (module_spec); + + // Don't look for the file if it appears to be the same one we already + // checked for above... + if (file_spec != module_file_spec) + { + if (!file_spec.Exists()) + { + file_spec.GetPath(path, sizeof(path)); + if (path[0] == '\0') + module_file_spec.GetPath(path, sizeof(path)); + if (file_spec.Exists()) + { + 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() = file_spec; + platform_module_spec.GetPlatformFileSpec() = file_spec; + ModuleList matching_module_list; + if (shared_module_list.FindModules (platform_module_spec, matching_module_list) > 0) + { + 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() == NULL) + { + TimeValue file_spec_mod_time(file_spec.GetModificationTime()); + if (file_spec_mod_time.IsValid()) + { + 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.get() == NULL) + { + module_sp.reset (new 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 (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + } + else + { + file_spec.GetPath(path, sizeof(path)); + + if (file_spec) + { + 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<Error>& errors, + Stream *feedback_stream, + bool continue_on_error) +{ + if (!target) + return false; + Mutex::Locker locker(m_modules_mutex); + for (auto module : m_modules) + { + Error 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.size() == 0; +} diff --git a/source/Core/Opcode.cpp b/source/Core/Opcode.cpp new file mode 100644 index 0000000..d987865 --- /dev/null +++ b/source/Core/Opcode.cpp @@ -0,0 +1,134 @@ +//===-- Opcode.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Opcode.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" + + +using namespace lldb; +using namespace lldb_private; + + +int +Opcode::Dump (Stream *s, uint32_t min_byte_width) +{ + int bytes_written = 0; + switch (m_type) + { + case Opcode::eTypeInvalid: + bytes_written = s->PutCString ("<invalid>"); + break; + case Opcode::eType8: + bytes_written = s->Printf ("0x%2.2x", m_data.inst8); + break; + case Opcode::eType16: + bytes_written = s->Printf ("0x%4.4x", m_data.inst16); + break; + case Opcode::eType16_2: + case Opcode::eType32: + bytes_written = s->Printf ("0x%8.8x", m_data.inst32); + break; + + case Opcode::eType64: + bytes_written = 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) + bytes_written += s->PutChar (' '); + bytes_written += s->Printf ("%2.2x", m_data.inst.bytes[i]); + } + } + break; + } + + // Add spaces to make sure bytes dispay comes out even in case opcodes + // aren't all the same size + if (bytes_written < min_byte_width) + bytes_written = s->Printf ("%*s", min_byte_width - bytes_written, ""); + return bytes_written; +} + +lldb::ByteOrder +Opcode::GetDataByteOrder () const +{ + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: + case Opcode::eType16: + case Opcode::eType16_2: + case Opcode::eType32: + case Opcode::eType64: return lldb::endian::InlHostByteOrder(); + case Opcode::eTypeBytes: + break; + } + return eByteOrderInvalid; +} + +uint32_t +Opcode::GetData (DataExtractor &data) const +{ + uint32_t byte_size = GetByteSize (); + + DataBufferSP buffer_sp; + if (byte_size > 0) + { + switch (m_type) + { + case Opcode::eTypeInvalid: + break; + + case Opcode::eType8: buffer_sp.reset (new DataBufferHeap (&m_data.inst8, byte_size)); break; + case Opcode::eType16: buffer_sp.reset (new DataBufferHeap (&m_data.inst16, byte_size)); break; + case Opcode::eType16_2: + { + // 32 bit thumb instruction, we need to sizzle this a bit + uint8_t buf[4]; + buf[0] = m_data.inst.bytes[2]; + buf[1] = m_data.inst.bytes[3]; + buf[2] = m_data.inst.bytes[0]; + buf[3] = m_data.inst.bytes[1]; + buffer_sp.reset (new DataBufferHeap (buf, byte_size)); + } + break; + case Opcode::eType32: + buffer_sp.reset (new DataBufferHeap (&m_data.inst32, byte_size)); + break; + case Opcode::eType64: buffer_sp.reset (new DataBufferHeap (&m_data.inst64, byte_size)); break; + case Opcode::eTypeBytes:buffer_sp.reset (new DataBufferHeap (GetOpcodeBytes(), byte_size)); break; + break; + } + } + + if (buffer_sp) + { + data.SetByteOrder(GetDataByteOrder()); + data.SetData (buffer_sp); + return byte_size; + } + data.Clear(); + return 0; +} + + + diff --git a/source/Core/PluginManager.cpp b/source/Core/PluginManager.cpp new file mode 100644 index 0000000..7a2d377 --- /dev/null +++ b/source/Core/PluginManager.cpp @@ -0,0 +1,2064 @@ +//===-- PluginManager.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/PluginManager.h" + +#include <limits.h> + +#include <string> +#include <vector> + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/OptionValueProperties.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +enum PluginAction +{ + ePluginRegisterInstance, + ePluginUnregisterInstance, + ePluginGetInstanceAtIndex +}; + + +typedef bool (*PluginInitCallback) (void); +typedef void (*PluginTermCallback) (void); + +struct PluginInfo +{ + void *plugin_handle; + PluginInitCallback plugin_init_callback; + PluginTermCallback plugin_term_callback; +}; + +typedef std::map<FileSpec, PluginInfo> PluginTerminateMap; + +static Mutex & +GetPluginMapMutex () +{ + static Mutex g_plugin_map_mutex (Mutex::eMutexTypeRecursive); + 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) +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetPluginMapMutex ()); + PluginTerminateMap &plugin_map = GetPluginMap (); + assert (plugin_map.find (plugin_file_spec) == plugin_map.end()); + plugin_map[plugin_file_spec] = plugin_info; +} + + +static FileSpec::EnumerateDirectoryResult +LoadPluginCallback +( + void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec +) +{ +// PluginManager *plugin_manager = (PluginManager *)baton; + Error error; + + // 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 (file_type == FileSpec::eFileTypeRegular || + file_type == FileSpec::eFileTypeSymbolicLink || + file_type == FileSpec::eFileTypeUnknown ) + { + FileSpec plugin_file_spec (file_spec); + plugin_file_spec.ResolvePath(); + + if (PluginIsLoaded (plugin_file_spec)) + return FileSpec::eEnumerateDirectoryResultNext; + else + { + PluginInfo plugin_info = { NULL, NULL, NULL }; + uint32_t flags = Host::eDynamicLibraryOpenOptionLazy | + Host::eDynamicLibraryOpenOptionLocal | + Host::eDynamicLibraryOpenOptionLimitGetSymbol; + + plugin_info.plugin_handle = Host::DynamicLibraryOpen (plugin_file_spec, flags, error); + if (plugin_info.plugin_handle) + { + bool success = false; + plugin_info.plugin_init_callback = (PluginInitCallback)Host::DynamicLibraryGetSymbol (plugin_info.plugin_handle, "LLDBPluginInitialize", error); + 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 NULL + plugin_info.plugin_term_callback = (PluginTermCallback)Host::DynamicLibraryGetSymbol (plugin_info.plugin_handle, "LLDBPluginTerminate", error); + } + 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. + Host::DynamicLibraryClose (plugin_info.plugin_handle); + plugin_info.plugin_handle = NULL; + plugin_info.plugin_init_callback = NULL; + } + + // 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 FileSpec::eEnumerateDirectoryResultNext; + } + } + } + + if (file_type == FileSpec::eFileTypeUnknown || + file_type == FileSpec::eFileTypeDirectory || + file_type == FileSpec::eFileTypeSymbolicLink ) + { + // 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 enurating a file system that doesn't have correct file type + // information. + return FileSpec::eEnumerateDirectoryResultEnter; + } + + return FileSpec::eEnumerateDirectoryResultNext; +} + + +void +PluginManager::Initialize () +{ +#if 1 + FileSpec dir_spec; + const bool find_directories = true; + const bool find_files = true; + const bool find_other = true; + char dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypeLLDBSystemPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + NULL); + } + } + + if (Host::GetLLDBPath (ePathTypeLLDBUserPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + NULL); + } + } +#endif +} + +void +PluginManager::Terminate () +{ + Mutex::Locker locker (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 NULL). + if (pos->second.plugin_handle) + { + if (pos->second.plugin_term_callback) + pos->second.plugin_term_callback(); + Host::DynamicLibraryClose (pos->second.plugin_handle); + } + } + plugin_map.clear(); +} + + +#pragma mark ABI + + +struct ABIInstance +{ + ABIInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + ABICreateInstance create_callback; +}; + +typedef std::vector<ABIInstance> ABIInstances; + +static Mutex & +GetABIInstancesMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ABIInstances & +GetABIInstances () +{ + static ABIInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetABIInstancesMutex ()); + GetABIInstances ().push_back (instance); + return true; + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ABICreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetABIInstancesMutex ()); + ABIInstances &instances = GetABIInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +#pragma mark Disassembler + + +struct DisassemblerInstance +{ + DisassemblerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + DisassemblerCreateInstance create_callback; +}; + +typedef std::vector<DisassemblerInstance> DisassemblerInstances; + +static Mutex & +GetDisassemblerMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static DisassemblerInstances & +GetDisassemblerInstances () +{ + static DisassemblerInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetDisassemblerMutex ()); + GetDisassemblerInstances ().push_back (instance); + return true; + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DisassemblerCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetDisassemblerMutex ()); + DisassemblerInstances &instances = GetDisassemblerInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + + +#pragma mark DynamicLoader + + +struct DynamicLoaderInstance +{ + DynamicLoaderInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback (NULL) + { + } + + ConstString name; + std::string description; + DynamicLoaderCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector<DynamicLoaderInstance> DynamicLoaderInstances; + + +static Mutex & +GetDynamicLoaderMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static DynamicLoaderInstances & +GetDynamicLoaderInstances () +{ + static DynamicLoaderInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetDynamicLoaderMutex ()); + GetDynamicLoaderInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DynamicLoaderCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetDynamicLoaderMutex ()); + DynamicLoaderInstances &instances = GetDynamicLoaderInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +#pragma mark EmulateInstruction + + +struct EmulateInstructionInstance +{ + EmulateInstructionInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + EmulateInstructionCreateInstance create_callback; +}; + +typedef std::vector<EmulateInstructionInstance> EmulateInstructionInstances; + +static Mutex & +GetEmulateInstructionMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static EmulateInstructionInstances & +GetEmulateInstructionInstances () +{ + static EmulateInstructionInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetEmulateInstructionMutex ()); + GetEmulateInstructionInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (EmulateInstructionCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetEmulateInstructionMutex ()); + EmulateInstructionInstances &instances = GetEmulateInstructionInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +EmulateInstructionCreateInstance +PluginManager::GetEmulateInstructionCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} +#pragma mark OperatingSystem + + +struct OperatingSystemInstance +{ + OperatingSystemInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + OperatingSystemCreateInstance create_callback; +}; + +typedef std::vector<OperatingSystemInstance> OperatingSystemInstances; + +static Mutex & +GetOperatingSystemMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static OperatingSystemInstances & +GetOperatingSystemInstances () +{ + static OperatingSystemInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + OperatingSystemCreateInstance create_callback) +{ + if (create_callback) + { + OperatingSystemInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetOperatingSystemMutex ()); + GetOperatingSystemInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (OperatingSystemCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetOperatingSystemMutex ()); + OperatingSystemInstances &instances = GetOperatingSystemInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +OperatingSystemCreateInstance +PluginManager::GetOperatingSystemCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +#pragma mark LanguageRuntime + + +struct LanguageRuntimeInstance +{ + LanguageRuntimeInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + LanguageRuntimeCreateInstance create_callback; +}; + +typedef std::vector<LanguageRuntimeInstance> LanguageRuntimeInstances; + +static Mutex & +GetLanguageRuntimeMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static LanguageRuntimeInstances & +GetLanguageRuntimeInstances () +{ + static LanguageRuntimeInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + LanguageRuntimeCreateInstance create_callback +) +{ + if (create_callback) + { + LanguageRuntimeInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + GetLanguageRuntimeInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LanguageRuntimeCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +LanguageRuntimeCreateInstance +PluginManager::GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +#pragma mark ObjectFile + +struct ObjectFileInstance +{ + ObjectFileInstance() : + name(), + description(), + create_callback(NULL), + create_memory_callback (NULL), + get_module_specifications (NULL) + { + } + + ConstString name; + std::string description; + ObjectFileCreateInstance create_callback; + ObjectFileCreateMemoryInstance create_memory_callback; + ObjectFileGetModuleSpecifications get_module_specifications; +}; + +typedef std::vector<ObjectFileInstance> ObjectFileInstances; + +static Mutex & +GetObjectFileMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ObjectFileInstances & +GetObjectFileInstances () +{ + static ObjectFileInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + ObjectFileCreateInstance create_callback, + ObjectFileCreateMemoryInstance create_memory_callback, + ObjectFileGetModuleSpecifications get_module_specifications) +{ + 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.get_module_specifications = get_module_specifications; + Mutex::Locker locker (GetObjectFileMutex ()); + GetObjectFileInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectFileCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +ObjectFileCreateMemoryInstance +PluginManager::GetObjectFileCreateMemoryCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].create_memory_callback; + return NULL; +} + +ObjectFileGetModuleSpecifications +PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].get_module_specifications; + return NULL; +} + +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +ObjectFileCreateMemoryInstance +PluginManager::GetObjectFileCreateMemoryCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + + +#pragma mark ObjectContainer + +struct ObjectContainerInstance +{ + ObjectContainerInstance() : + name(), + description(), + create_callback (NULL), + get_module_specifications (NULL) + { + } + + ConstString name; + std::string description; + ObjectContainerCreateInstance create_callback; + ObjectFileGetModuleSpecifications get_module_specifications; + +}; + +typedef std::vector<ObjectContainerInstance> ObjectContainerInstances; + +static Mutex & +GetObjectContainerMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ObjectContainerInstances & +GetObjectContainerInstances () +{ + static ObjectContainerInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin (const 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; + Mutex::Locker locker (GetObjectContainerMutex ()); + GetObjectContainerInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectContainerCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +ObjectFileGetModuleSpecifications +PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + if (idx < instances.size()) + return instances[idx].get_module_specifications; + return NULL; +} + +#pragma mark LogChannel + +struct LogInstance +{ + LogInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + LogChannelCreateInstance create_callback; +}; + +typedef std::vector<LogInstance> LogInstances; + +static Mutex & +GetLogMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static LogInstances & +GetLogInstances () +{ + static LogInstances g_instances; + return g_instances; +} + + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + LogChannelCreateInstance create_callback +) +{ + if (create_callback) + { + LogInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetLogMutex ()); + GetLogInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LogChannelCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + + LogInstances::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; +} + +const char * +PluginManager::GetLogChannelCreateNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + + LogInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark Platform + +struct PlatformInstance +{ + PlatformInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback (NULL) + { + } + + ConstString name; + std::string description; + PlatformCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector<PlatformInstance> PlatformInstances; + +static Mutex & +GetPlatformInstancesMutex () +{ + static Mutex g_platform_instances_mutex (Mutex::eMutexTypeRecursive); + return g_platform_instances_mutex; +} + +static PlatformInstances & +GetPlatformInstances () +{ + static PlatformInstances g_platform_instances; + return g_platform_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + PlatformCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + +const char * +PluginManager::GetPlatformPluginDescriptionAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].description.c_str(); + return NULL; +} + +bool +PluginManager::UnregisterPlugin (PlatformCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +PlatformCreateInstance +PluginManager::GetPlatformCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +size_t +PluginManager::AutoCompletePlatformName (const char *name, StringList &matches) +{ + if (name) + { + Mutex::Locker locker (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)) + matches.AppendString (plugin_name.data()); + } + } + return matches.GetSize(); +} +#pragma mark Process + +struct ProcessInstance +{ + ProcessInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback(NULL) + { + } + + ConstString name; + std::string description; + ProcessCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector<ProcessInstance> ProcessInstances; + +static Mutex & +GetProcessMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ProcessInstances & +GetProcessInstances () +{ + static ProcessInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin (const 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; + Mutex::Locker locker (GetProcessMutex ()); + GetProcessInstances ().push_back (instance); + } + return false; +} + +const char * +PluginManager::GetProcessPluginNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + +const char * +PluginManager::GetProcessPluginDescriptionAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].description.c_str(); + return NULL; +} + +bool +PluginManager::UnregisterPlugin (ProcessCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +#pragma mark SymbolFile + +struct SymbolFileInstance +{ + SymbolFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SymbolFileCreateInstance create_callback; +}; + +typedef std::vector<SymbolFileInstance> SymbolFileInstances; + +static Mutex & +GetSymbolFileMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SymbolFileInstances & +GetSymbolFileInstances () +{ + static SymbolFileInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + SymbolFileCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolFileInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetSymbolFileMutex ()); + GetSymbolFileInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolFileCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetSymbolFileMutex ()); + SymbolFileInstances &instances = GetSymbolFileInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + + +#pragma mark SymbolVendor + +struct SymbolVendorInstance +{ + SymbolVendorInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SymbolVendorCreateInstance create_callback; +}; + +typedef std::vector<SymbolVendorInstance> SymbolVendorInstances; + +static Mutex & +GetSymbolVendorMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SymbolVendorInstances & +GetSymbolVendorInstances () +{ + static SymbolVendorInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetSymbolVendorMutex ()); + GetSymbolVendorInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolVendorCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetSymbolVendorMutex ()); + SymbolVendorInstances &instances = GetSymbolVendorInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +#pragma mark UnwindAssembly + +struct UnwindAssemblyInstance +{ + UnwindAssemblyInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + UnwindAssemblyCreateInstance create_callback; +}; + +typedef std::vector<UnwindAssemblyInstance> UnwindAssemblyInstances; + +static Mutex & +GetUnwindAssemblyMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static UnwindAssemblyInstances & +GetUnwindAssemblyInstances () +{ + static UnwindAssemblyInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + GetUnwindAssemblyInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (UnwindAssemblyCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +UnwindAssemblyCreateInstance +PluginManager::GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +void +PluginManager::DebuggerInitialize (Debugger &debugger) +{ + // Initialize the DynamicLoader plugins + { + Mutex::Locker locker (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 Platform plugins + { + Mutex::Locker locker (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 + { + Mutex::Locker locker (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); + } + } + +} + +// 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, + const ConstString &plugin_type_name, + const 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 (NULL, g_property_name); + if (!plugin_properties_sp && can_create) + { + plugin_properties_sp.reset (new 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 (NULL, plugin_type_name); + if (!plugin_type_properties_sp && can_create) + { + plugin_type_properties_sp.reset (new 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, + const ConstString &plugin_type_name, + const 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 (NULL, plugin_type_name); + if (!plugin_properties_sp && can_create) + { + plugin_properties_sp.reset (new 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 (NULL, g_property_name); + if (!plugin_type_properties_sp && can_create) + { + plugin_type_properties_sp.reset (new 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(); +} + + +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForDynamicLoaderPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("dynamic-loader"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForDynamicLoaderPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("dynamic-loader"), + ConstString("Settings for dynamic loader 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::GetSettingForPlatformPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPluginsOldStyle (debugger, + ConstString("platform"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForPlatformPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPluginsOldStyle (debugger, + ConstString("platform"), + ConstString("Settings for platform 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::GetSettingForProcessPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("process"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForProcessPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("process"), + ConstString("Settings for process 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; +} + diff --git a/source/Core/RegisterValue.cpp b/source/Core/RegisterValue.cpp new file mode 100644 index 0000000..91f5bea --- /dev/null +++ b/source/Core/RegisterValue.cpp @@ -0,0 +1,1272 @@ +//===-- RegisterValue.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RegisterValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + + +bool +RegisterValue::Dump (Stream *s, + const RegisterInfo *reg_info, + bool prefix_with_name, + bool prefix_with_alt_name, + Format format, + uint32_t reg_name_right_align_at) const +{ + DataExtractor data; + if (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"); + const char *fmt = format_string.GetData(); + if (prefix_with_name) + { + if (reg_info->name) + { + s->Printf (fmt, reg_info->name); + name_printed = true; + } + else if (reg_info->alt_name) + { + s->Printf (fmt, 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, 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, reg_info->name); + name_printed = true; + } + } + if (name_printed) + s->PutCString (" = "); + + if (format == eFormatDefault) + format = reg_info->format; + + data.Dump (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; +} + + +bool +RegisterValue::GetData (DataExtractor &data) const +{ + return data.SetData(GetBytes(), GetByteSize(), GetByteOrder()) > 0; +} + + +uint32_t +RegisterValue::GetAsMemoryData (const RegisterInfo *reg_info, + void *dst, + uint32_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const +{ + if (reg_info == NULL) + { + error.SetErrorString ("invalid register info argument."); + return 0; + } + + // ReadRegister should have already been called on tgus object prior to + // calling this. + if (GetType() == eTypeInvalid) + { + // No value has been read into this object... + error.SetErrorStringWithFormat("invalid register value type for register %s", reg_info->name); + return 0; + } + + if (dst_len > kMaxRegisterByteSize) + { + error.SetErrorString ("destination is too big"); + return 0; + } + + const uint32_t src_len = reg_info->byte_size; + + // Extract the register data into a data extractor + DataExtractor reg_data; + if (!GetData(reg_data)) + { + error.SetErrorString ("invalid register value to copy into"); + return 0; + } + + // Prepare a memory buffer that contains some or all of the register value + const uint32_t bytes_copied = reg_data.CopyByteOrderedData (0, // src offset + src_len, // src length + dst, // dst buffer + dst_len, // dst length + dst_byte_order); // dst byte order + if (bytes_copied == 0) + error.SetErrorStringWithFormat("failed to copy data for register write of %s", reg_info->name); + + return bytes_copied; +} + +uint32_t +RegisterValue::SetFromMemoryData (const RegisterInfo *reg_info, + const void *src, + uint32_t src_len, + lldb::ByteOrder src_byte_order, + Error &error) +{ + if (reg_info == NULL) + { + error.SetErrorString ("invalid register info argument."); + return 0; + } + + // Moving from addr into a register + // + // Case 1: src_len == dst_len + // + // |AABBCCDD| Address contents + // |AABBCCDD| Register contents + // + // Case 2: src_len > dst_len + // + // Error! (The register should always be big enough to hold the data) + // + // Case 3: src_len < dst_len + // + // |AABB| Address contents + // |AABB0000| Register contents [on little-endian hardware] + // |0000AABB| Register contents [on big-endian hardware] + if (src_len > kMaxRegisterByteSize) + { + error.SetErrorStringWithFormat ("register buffer is too small to receive %u bytes of data.", src_len); + return 0; + } + + const uint32_t dst_len = reg_info->byte_size; + + if (src_len > dst_len) + { + error.SetErrorStringWithFormat("%u bytes is too big to store in register %s (%u bytes)", src_len, reg_info->name, dst_len); + return 0; + } + + // Use a data extractor to correctly copy and pad the bytes read into the + // register value + DataExtractor src_data (src, src_len, src_byte_order, 4); + + // Given the register info, set the value type of this RegisterValue object + SetType (reg_info); + // And make sure we were able to figure out what that register value was + RegisterValue::Type value_type = GetType(); + if (value_type == eTypeInvalid) + { + // No value has been read into this object... + error.SetErrorStringWithFormat("invalid register value type for register %s", reg_info->name); + return 0; + } + else if (value_type == eTypeBytes) + { + m_data.buffer.byte_order = src_byte_order; + // Make sure to set the buffer length of the destination buffer to avoid + // problems due to uninitalized variables. + m_data.buffer.length = src_len; + } + + const uint32_t bytes_copied = src_data.CopyByteOrderedData (0, // src offset + src_len, // src length + GetBytes(), // dst buffer + GetByteSize(), // dst length + GetByteOrder()); // dst byte order + if (bytes_copied == 0) + error.SetErrorStringWithFormat("failed to copy data for register write of %s", reg_info->name); + + return bytes_copied; +} + +bool +RegisterValue::GetScalarValue (Scalar &scalar) const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: scalar = m_data.uint8; return true; + case 2: scalar = m_data.uint16; return true; + case 4: scalar = m_data.uint32; return true; + case 8: scalar = m_data.uint64; return true; + } + } + case eTypeUInt8: scalar = m_data.uint8; return true; + case eTypeUInt16: scalar = m_data.uint16; return true; + case eTypeUInt32: scalar = m_data.uint32; return true; + case eTypeUInt64: scalar = m_data.uint64; return true; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: break; +#endif + case eTypeFloat: scalar = m_data.ieee_float; return true; + case eTypeDouble: scalar = m_data.ieee_double; return true; + case eTypeLongDouble: scalar = m_data.ieee_long_double; return true; + } + return false; +} + +void +RegisterValue::Clear() +{ + m_type = eTypeInvalid; +} + +RegisterValue::Type +RegisterValue::SetType (const RegisterInfo *reg_info) +{ + m_type = eTypeInvalid; + const uint32_t byte_size = reg_info->byte_size; + switch (reg_info->encoding) + { + case eEncodingInvalid: + break; + + case eEncodingUint: + case eEncodingSint: + if (byte_size == 1) + m_type = eTypeUInt8; + else if (byte_size <= 2) + m_type = eTypeUInt16; + else if (byte_size <= 4) + m_type = eTypeUInt32; + else if (byte_size <= 8) + m_type = eTypeUInt64; +#if defined (ENABLE_128_BIT_SUPPORT) + else if (byte_size <= 16) + m_type = eTypeUInt128; +#endif + break; + + case eEncodingIEEE754: + if (byte_size == sizeof(float)) + m_type = eTypeFloat; + else if (byte_size == sizeof(double)) + m_type = eTypeDouble; + else if (byte_size == sizeof(long double)) + m_type = eTypeLongDouble; + break; + + case eEncodingVector: + m_type = eTypeBytes; + break; + } + return m_type; +} + +Error +RegisterValue::SetValueFromData (const RegisterInfo *reg_info, DataExtractor &src, lldb::offset_t src_offset, bool partial_data_ok) +{ + Error error; + + if (src.GetByteSize() == 0) + { + error.SetErrorString ("empty data."); + return error; + } + + if (reg_info->byte_size == 0) + { + error.SetErrorString ("invalid register info."); + return error; + } + + uint32_t src_len = src.GetByteSize() - src_offset; + + if (!partial_data_ok && (src_len < reg_info->byte_size)) + { + error.SetErrorString ("not enough data."); + return error; + } + + // Cap the data length if there is more than enough bytes for this register + // value + if (src_len > reg_info->byte_size) + src_len = reg_info->byte_size; + + // Zero out the value in case we get partial data... + memset (m_data.buffer.bytes, 0, sizeof (m_data.buffer.bytes)); + + switch (SetType (reg_info)) + { + case eTypeInvalid: + error.SetErrorString(""); + break; + case eTypeUInt8: SetUInt8 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt16: SetUInt16 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt32: SetUInt32 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt64: SetUInt64 (src.GetMaxU64 (&src_offset, src_len)); break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + { + __uint128_t data1 = src.GetU64 (&src_offset); + __uint128_t data2 = src.GetU64 (&src_offset); + if (src.GetByteSize() == eByteOrderBig) + SetUInt128 (data1 << 64 + data2); + else + SetUInt128 (data2 << 64 + data1); + } + break; +#endif + case eTypeFloat: SetFloat (src.GetFloat (&src_offset)); break; + case eTypeDouble: SetDouble(src.GetDouble (&src_offset)); break; + case eTypeLongDouble: SetFloat (src.GetLongDouble (&src_offset)); break; + case eTypeBytes: + { + m_data.buffer.length = reg_info->byte_size; + m_data.buffer.byte_order = src.GetByteOrder(); + assert (m_data.buffer.length <= kMaxRegisterByteSize); + if (m_data.buffer.length > kMaxRegisterByteSize) + m_data.buffer.length = kMaxRegisterByteSize; + if (src.CopyByteOrderedData (src_offset, // offset within "src" to start extracting data + src_len, // src length + m_data.buffer.bytes, // dst buffer + m_data.buffer.length, // dst length + m_data.buffer.byte_order) == 0)// dst byte order + { + error.SetErrorString ("data copy failed data."); + return error; + } + } + } + + return error; +} + +#include "llvm/ADT/StringRef.h" +#include <vector> +static inline void StripSpaces(llvm::StringRef &Str) +{ + while (!Str.empty() && isspace(Str[0])) + Str = Str.substr(1); + while (!Str.empty() && isspace(Str.back())) + Str = Str.substr(0, Str.size()-1); +} +static inline void LStrip(llvm::StringRef &Str, char c) +{ + if (!Str.empty() && Str.front() == c) + Str = Str.substr(1); +} +static inline void RStrip(llvm::StringRef &Str, char c) +{ + if (!Str.empty() && Str.back() == c) + Str = Str.substr(0, Str.size()-1); +} +// Helper function for RegisterValue::SetValueFromCString() +static bool +ParseVectorEncoding(const RegisterInfo *reg_info, const char *vector_str, const uint32_t byte_size, RegisterValue *reg_value) +{ + // Example: vector_str = "{0x2c 0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f 0x2a 0x3e}". + llvm::StringRef Str(vector_str); + StripSpaces(Str); + LStrip(Str, '{'); + RStrip(Str, '}'); + StripSpaces(Str); + + char Sep = ' '; + + // The first split should give us: + // ('0x2c', '0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f 0x2a 0x3e'). + std::pair<llvm::StringRef, llvm::StringRef> Pair = Str.split(Sep); + std::vector<uint8_t> bytes; + unsigned byte = 0; + + // Using radix auto-sensing by passing 0 as the radix. + // Keep on processing the vector elements as long as the parsing succeeds and the vector size is < byte_size. + while (!Pair.first.getAsInteger(0, byte) && bytes.size() < byte_size) { + bytes.push_back(byte); + Pair = Pair.second.split(Sep); + } + + // Check for vector of exact byte_size elements. + if (bytes.size() != byte_size) + return false; + + reg_value->SetBytes(&(bytes.front()), byte_size, eByteOrderLittle); + return true; +} +Error +RegisterValue::SetValueFromCString (const RegisterInfo *reg_info, const char *value_str) +{ + Error error; + if (reg_info == NULL) + { + error.SetErrorString ("Invalid register info argument."); + return error; + } + + if (value_str == NULL || value_str[0] == '\0') + { + error.SetErrorString ("Invalid c-string value string."); + return error; + } + bool success = false; + const uint32_t byte_size = reg_info->byte_size; + switch (reg_info->encoding) + { + case eEncodingInvalid: + error.SetErrorString ("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size <= sizeof (uint64_t)) + { + uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); + else if (!Args::UInt64ValueIsValidForByteSize (uval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %u byte unsigned integer value", uval64, byte_size); + else + { + if (!SetUInt (uval64, reg_info->byte_size)) + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %u", byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %u", byte_size); + return error; + } + break; + + case eEncodingSint: + if (byte_size <= sizeof (long long)) + { + uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); + else if (!Args::SInt64ValueIsValidForByteSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %u byte signed integer value", sval64, byte_size); + else + { + if (!SetUInt (sval64, reg_info->byte_size)) + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %u", byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %u", byte_size); + return error; + } + break; + + case eEncodingIEEE754: + if (byte_size == sizeof (float)) + { + if (::sscanf (value_str, "%f", &m_data.ieee_float) == 1) + m_type = eTypeFloat; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (double)) + { + if (::sscanf (value_str, "%lf", &m_data.ieee_double) == 1) + m_type = eTypeDouble; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (long double)) + { + if (::sscanf (value_str, "%Lf", &m_data.ieee_long_double) == 1) + m_type = eTypeLongDouble; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else + { + error.SetErrorStringWithFormat ("unsupported float byte size: %u", byte_size); + return error; + } + break; + + case eEncodingVector: + if (!ParseVectorEncoding(reg_info, value_str, byte_size, this)) + error.SetErrorString ("unrecognized vector encoding string value."); + break; + } + if (error.Fail()) + m_type = eTypeInvalid; + + return error; +} + + +bool +RegisterValue::SignExtend (uint32_t sign_bitpos) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (sign_bitpos == (8-1)) + return true; + else if (sign_bitpos < (8-1)) + { + uint8_t sign_bit = 1u << sign_bitpos; + if (m_data.uint8 & sign_bit) + { + const uint8_t mask = ~(sign_bit) + 1u; + m_data.uint8 |= mask; + } + return true; + } + break; + + case eTypeUInt16: + if (sign_bitpos == (16-1)) + return true; + else if (sign_bitpos < (16-1)) + { + uint16_t sign_bit = 1u << sign_bitpos; + if (m_data.uint16 & sign_bit) + { + const uint16_t mask = ~(sign_bit) + 1u; + m_data.uint16 |= mask; + } + return true; + } + break; + + case eTypeUInt32: + if (sign_bitpos == (32-1)) + return true; + else if (sign_bitpos < (32-1)) + { + uint32_t sign_bit = 1u << sign_bitpos; + if (m_data.uint32 & sign_bit) + { + const uint32_t mask = ~(sign_bit) + 1u; + m_data.uint32 |= mask; + } + return true; + } + break; + + case eTypeUInt64: + if (sign_bitpos == (64-1)) + return true; + else if (sign_bitpos < (64-1)) + { + uint64_t sign_bit = 1ull << sign_bitpos; + if (m_data.uint64 & sign_bit) + { + const uint64_t mask = ~(sign_bit) + 1ull; + m_data.uint64 |= mask; + } + return true; + } + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sign_bitpos == (128-1)) + return true; + else if (sign_bitpos < (128-1)) + { + __uint128_t sign_bit = (__uint128_t)1u << sign_bitpos; + if (m_data.uint128 & sign_bit) + { + const uint128_t mask = ~(sign_bit) + 1u; + m_data.uint128 |= mask; + } + return true; + } + break; +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + case eTypeBytes: + break; + } + return false; +} + +bool +RegisterValue::CopyValue (const RegisterValue &rhs) +{ + m_type = rhs.m_type; + switch (m_type) + { + case eTypeInvalid: + return false; + case eTypeUInt8: m_data.uint8 = rhs.m_data.uint8; break; + case eTypeUInt16: m_data.uint16 = rhs.m_data.uint16; break; + case eTypeUInt32: m_data.uint32 = rhs.m_data.uint32; break; + case eTypeUInt64: m_data.uint64 = rhs.m_data.uint64; break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: m_data.uint128 = rhs.m_data.uint128; break; +#endif + case eTypeFloat: m_data.ieee_float = rhs.m_data.ieee_float; break; + case eTypeDouble: m_data.ieee_double = rhs.m_data.ieee_double; break; + case eTypeLongDouble: m_data.ieee_long_double = rhs.m_data.ieee_long_double; break; + case eTypeBytes: + assert (rhs.m_data.buffer.length <= kMaxRegisterByteSize); + ::memcpy (m_data.buffer.bytes, rhs.m_data.buffer.bytes, kMaxRegisterByteSize); + m_data.buffer.length = rhs.m_data.buffer.length; + m_data.buffer.byte_order = rhs.m_data.buffer.byte_order; + break; + } + return true; +} + +uint16_t +RegisterValue::GetAsUInt16 (uint16_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint32_t +RegisterValue::GetAsUInt32 (uint32_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeFloat: + if (sizeof(float) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +uint64_t +RegisterValue::GetAsUInt64 (uint64_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeUInt64: return m_data.uint64; + case eTypeFloat: + if (sizeof(float) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + case 8: return m_data.uint64; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +#if defined (ENABLE_128_BIT_SUPPORT) +__uint128_t +RegisterValue::GetAsUInt128 (__uint128_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeUInt64: return m_data.uint64; + case eTypeUInt128: return m_data.uint128; + case eTypeFloat: + if (sizeof(float) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: + break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + case 8: return m_data.uint64; + case 16: return m_data.uint128; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} +#endif +float +RegisterValue::GetAsFloat (float fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt32: + if (sizeof(float) == sizeof(m_data.uint32)) + return m_data.ieee_float; + break; + case eTypeUInt64: + if (sizeof(float) == sizeof(m_data.uint64)) + return m_data.ieee_float; + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(float) == sizeof(m_data.uint128)) + return m_data.ieee_float; + break; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: + if (sizeof(float) == sizeof(double)) + return m_data.ieee_float; + break; + case eTypeLongDouble: + if (sizeof(float) == sizeof(long double)) + return m_data.ieee_float; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +double +RegisterValue::GetAsDouble (double fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: + break; + + case eTypeUInt32: + if (sizeof(double) == sizeof(m_data.uint32)) + return m_data.ieee_double; + break; + + case eTypeUInt64: + if (sizeof(double) == sizeof(m_data.uint64)) + return m_data.ieee_double; + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(double) == sizeof(m_data.uint128)) + return m_data.ieee_double; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double; + + case eTypeLongDouble: + if (sizeof(double) == sizeof(long double)) + return m_data.ieee_double; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +long double +RegisterValue::GetAsLongDouble (long double fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: + break; + + case eTypeUInt32: + if (sizeof(long double) == sizeof(m_data.uint32)) + return m_data.ieee_long_double; + break; + + case eTypeUInt64: + if (sizeof(long double) == sizeof(m_data.uint64)) + return m_data.ieee_long_double; + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(long double) == sizeof(m_data.uint128)) + return m_data.ieee_long_double; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +const void * +RegisterValue::GetBytes () const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return &m_data.uint8; + case eTypeUInt16: return &m_data.uint16; + case eTypeUInt32: return &m_data.uint32; + case eTypeUInt64: return &m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return &m_data.uint128; +#endif + case eTypeFloat: return &m_data.ieee_float; + case eTypeDouble: return &m_data.ieee_double; + case eTypeLongDouble: return &m_data.ieee_long_double; + case eTypeBytes: return m_data.buffer.bytes; + } + return NULL; +} + +void * +RegisterValue::GetBytes () +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return &m_data.uint8; + case eTypeUInt16: return &m_data.uint16; + case eTypeUInt32: return &m_data.uint32; + case eTypeUInt64: return &m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return &m_data.uint128; +#endif + case eTypeFloat: return &m_data.ieee_float; + case eTypeDouble: return &m_data.ieee_double; + case eTypeLongDouble: return &m_data.ieee_long_double; + case eTypeBytes: return m_data.buffer.bytes; + } + return NULL; +} + +uint32_t +RegisterValue::GetByteSize () const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return sizeof(m_data.uint8); + case eTypeUInt16: return sizeof(m_data.uint16); + case eTypeUInt32: return sizeof(m_data.uint32); + case eTypeUInt64: return sizeof(m_data.uint64); +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return sizeof(m_data.uint128); +#endif + case eTypeFloat: return sizeof(m_data.ieee_float); + case eTypeDouble: return sizeof(m_data.ieee_double); + case eTypeLongDouble: return sizeof(m_data.ieee_long_double); + case eTypeBytes: return m_data.buffer.length; + } + return 0; +} + + +bool +RegisterValue::SetUInt (uint64_t uint, uint32_t byte_size) +{ + if (byte_size == 0) + { + SetUInt64 (uint); + } + else if (byte_size == 1) + { + SetUInt8 (uint); + } + else if (byte_size <= 2) + { + SetUInt16 (uint); + } + else if (byte_size <= 4) + { + SetUInt32 (uint); + } + else if (byte_size <= 8) + { + SetUInt64 (uint); + } +#if defined (ENABLE_128_BIT_SUPPORT) + else if (byte_size <= 16) + { + SetUInt128 (uint); + } +#endif + else + return false; + return true; +} + +void +RegisterValue::SetBytes (const void *bytes, size_t length, lldb::ByteOrder byte_order) +{ + // If this assertion fires off we need to increase the size of + // m_data.buffer.bytes, or make it something that is allocated on + // the heap. Since the data buffer is in a union, we can't make it + // a collection class like SmallVector... + if (bytes && length > 0) + { + assert (length <= sizeof (m_data.buffer.bytes) && "Storing too many bytes in a RegisterValue."); + m_type = eTypeBytes; + m_data.buffer.length = length; + memcpy (m_data.buffer.bytes, bytes, length); + m_data.buffer.byte_order = byte_order; + } + else + { + m_type = eTypeInvalid; + m_data.buffer.length = 0; + } +} + + +bool +RegisterValue::operator == (const RegisterValue &rhs) const +{ + if (m_type == rhs.m_type) + { + switch (m_type) + { + case eTypeInvalid: return true; + case eTypeUInt8: return m_data.uint8 == rhs.m_data.uint8; + case eTypeUInt16: return m_data.uint16 == rhs.m_data.uint16; + case eTypeUInt32: return m_data.uint32 == rhs.m_data.uint32; + case eTypeUInt64: return m_data.uint64 == rhs.m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return m_data.uint128 == rhs.m_data.uint128; +#endif + case eTypeFloat: return m_data.ieee_float == rhs.m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double == rhs.m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double == rhs.m_data.ieee_long_double; + case eTypeBytes: + if (m_data.buffer.length != rhs.m_data.buffer.length) + return false; + else + { + uint8_t length = m_data.buffer.length; + if (length > kMaxRegisterByteSize) + length = kMaxRegisterByteSize; + return memcmp (m_data.buffer.bytes, rhs.m_data.buffer.bytes, length) == 0; + } + break; + } + } + return false; +} + +bool +RegisterValue::operator != (const RegisterValue &rhs) const +{ + if (m_type != rhs.m_type) + return true; + switch (m_type) + { + case eTypeInvalid: return false; + case eTypeUInt8: return m_data.uint8 != rhs.m_data.uint8; + case eTypeUInt16: return m_data.uint16 != rhs.m_data.uint16; + case eTypeUInt32: return m_data.uint32 != rhs.m_data.uint32; + case eTypeUInt64: return m_data.uint64 != rhs.m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return m_data.uint128 != rhs.m_data.uint128; +#endif + case eTypeFloat: return m_data.ieee_float != rhs.m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double != rhs.m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double != rhs.m_data.ieee_long_double; + case eTypeBytes: + if (m_data.buffer.length != rhs.m_data.buffer.length) + { + return true; + } + else + { + uint8_t length = m_data.buffer.length; + if (length > kMaxRegisterByteSize) + length = kMaxRegisterByteSize; + return memcmp (m_data.buffer.bytes, rhs.m_data.buffer.bytes, length) != 0; + } + break; + } + return true; +} + +bool +RegisterValue::ClearBit (uint32_t bit) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (bit < 8) + { + m_data.uint8 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt16: + if (bit < 16) + { + m_data.uint16 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt32: + if (bit < 32) + { + m_data.uint32 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt64: + if (bit < 64) + { + m_data.uint64 &= ~(1ull << (uint64_t)bit); + return true; + } + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (bit < 64) + { + m_data.uint128 &= ~((__uint128_t)1ull << (__uint128_t)bit); + return true; + } +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (m_data.buffer.byte_order == eByteOrderBig || m_data.buffer.byte_order == eByteOrderLittle) + { + uint32_t byte_idx; + if (m_data.buffer.byte_order == eByteOrderBig) + byte_idx = m_data.buffer.length - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < m_data.buffer.length) + { + m_data.buffer.bytes[byte_idx] &= ~(1u << byte_bit); + return true; + } + } + break; + } + return false; +} + + +bool +RegisterValue::SetBit (uint32_t bit) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (bit < 8) + { + m_data.uint8 |= (1u << bit); + return true; + } + break; + + case eTypeUInt16: + if (bit < 16) + { + m_data.uint16 |= (1u << bit); + return true; + } + break; + + case eTypeUInt32: + if (bit < 32) + { + m_data.uint32 |= (1u << bit); + return true; + } + break; + + case eTypeUInt64: + if (bit < 64) + { + m_data.uint64 |= (1ull << (uint64_t)bit); + return true; + } + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (bit < 64) + { + m_data.uint128 |= ((__uint128_t)1ull << (__uint128_t)bit); + return true; + } +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (m_data.buffer.byte_order == eByteOrderBig || m_data.buffer.byte_order == eByteOrderLittle) + { + uint32_t byte_idx; + if (m_data.buffer.byte_order == eByteOrderBig) + byte_idx = m_data.buffer.length - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < m_data.buffer.length) + { + m_data.buffer.bytes[byte_idx] |= (1u << byte_bit); + return true; + } + } + break; + } + return false; +} + diff --git a/source/Core/RegularExpression.cpp b/source/Core/RegularExpression.cpp new file mode 100644 index 0000000..4ccd774 --- /dev/null +++ b/source/Core/RegularExpression.cpp @@ -0,0 +1,279 @@ +//===-- RegularExpression.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RegularExpression.h" +#include "llvm/ADT/StringRef.h" +#include <string.h> + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +RegularExpression::RegularExpression() : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(REG_EXTENDED) +{ + memset(&m_preg,0,sizeof(m_preg)); +} + +//---------------------------------------------------------------------- +// Constructor that compiles "re" using "flags" and stores the +// resulting compiled regular expression into this object. +//---------------------------------------------------------------------- +RegularExpression::RegularExpression(const char* re, int flags) : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(flags) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re); +} + +//---------------------------------------------------------------------- +// Constructor that compiles "re" using "flags" and stores the +// resulting compiled regular expression into this object. +//---------------------------------------------------------------------- +RegularExpression::RegularExpression(const char* re) : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(REG_EXTENDED) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re); +} + +RegularExpression::RegularExpression(const RegularExpression &rhs) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(rhs.GetText(), rhs.GetCompileFlags()); +} + +const RegularExpression & +RegularExpression::operator= (const RegularExpression &rhs) +{ + if (&rhs != this) + { + Compile (rhs.GetText(), rhs.GetCompileFlags()); + } + return *this; +} +//---------------------------------------------------------------------- +// Destructor +// +// Any previosuly compiled regular expression contained in this +// object will be freed. +//---------------------------------------------------------------------- +RegularExpression::~RegularExpression() +{ + Free(); +} + +//---------------------------------------------------------------------- +// Compile a regular expression using the supplied regular +// expression text and flags. The compied regular expression lives +// in this object so that it can be readily used for regular +// expression matches. Execute() can be called after the regular +// expression is compiled. Any previosuly compiled regular +// expression contained in this object will be freed. +// +// RETURNS +// True of the refular expression compiles successfully, false +// otherwise. +//---------------------------------------------------------------------- +bool +RegularExpression::Compile(const char* re) +{ + return Compile (re, m_compile_flags); +} + +bool +RegularExpression::Compile(const char* re, int flags) +{ + Free(); + m_compile_flags = flags; + + if (re && re[0]) + { + m_re = re; + m_comp_err = ::regcomp (&m_preg, re, flags); + } + else + { + // No valid regular expression + m_comp_err = 1; + } + + return m_comp_err == 0; +} + +//---------------------------------------------------------------------- +// Execute a regular expression match using the compiled regular +// expression that is already in this object against the match +// string "s". If any parens are used for regular expression +// matches "match_count" should indicate the number of regmatch_t +// values that are present in "match_ptr". The regular expression +// will be executed using the "execute_flags". +//--------------------------------------------------------------------- +bool +RegularExpression::Execute(const char* s, Match *match, int execute_flags) const +{ + int err = 1; + if (s != NULL && m_comp_err == 0) + { + if (match) + { + err = ::regexec (&m_preg, + s, + match->GetSize(), + match->GetData(), + execute_flags); + } + else + { + err = ::regexec (&m_preg, + s, + 0, + NULL, + execute_flags); + } + } + + if (err != 0) + { + // The regular expression didn't compile, so clear the matches + if (match) + match->Clear(); + return false; + } + return true; +} + +bool +RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, std::string& match_str) const +{ + if (idx < m_matches.size()) + { + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) + { + // Matched the empty string... + match_str.clear(); + return true; + } + else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) + { + match_str.assign (s + m_matches[idx].rm_so, + m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool +RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, llvm::StringRef& match_str) const +{ + if (idx < m_matches.size()) + { + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) + { + // Matched the empty string... + match_str = llvm::StringRef(); + return true; + } + else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) + { + match_str = llvm::StringRef (s + m_matches[idx].rm_so, m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool +RegularExpression::Match::GetMatchSpanningIndices (const char* s, uint32_t idx1, uint32_t idx2, llvm::StringRef& match_str) const +{ + if (idx1 < m_matches.size() && idx2 < m_matches.size()) + { + if (m_matches[idx1].rm_so == m_matches[idx2].rm_eo) + { + // Matched the empty string... + match_str = llvm::StringRef(); + return true; + } + else if (m_matches[idx1].rm_so < m_matches[idx2].rm_eo) + { + match_str = llvm::StringRef (s + m_matches[idx1].rm_so, m_matches[idx2].rm_eo - m_matches[idx1].rm_so); + return true; + } + } + return false; +} + + +//---------------------------------------------------------------------- +// Returns true if the regular expression compiled and is ready +// for execution. +//---------------------------------------------------------------------- +bool +RegularExpression::IsValid () const +{ + return m_comp_err == 0; +} + +//---------------------------------------------------------------------- +// Returns the text that was used to compile the current regular +// expression. +//---------------------------------------------------------------------- +const char* +RegularExpression::GetText () const +{ + if (m_re.empty()) + return NULL; + return m_re.c_str(); +} + +//---------------------------------------------------------------------- +// Free any contained compiled regular expressions. +//---------------------------------------------------------------------- +void +RegularExpression::Free() +{ + if (m_comp_err == 0) + { + m_re.clear(); + regfree(&m_preg); + // Set a compile error since we no longer have a valid regex + m_comp_err = 1; + } +} + +size_t +RegularExpression::GetErrorAsCString (char *err_str, size_t err_str_max_len) const +{ + if (m_comp_err == 0) + { + if (err_str && err_str_max_len) + *err_str = '\0'; + return 0; + } + + return ::regerror (m_comp_err, &m_preg, err_str, err_str_max_len); +} + +bool +RegularExpression::operator < (const RegularExpression& rhs) const +{ + return (m_re < rhs.m_re); +} + diff --git a/source/Core/Scalar.cpp b/source/Core/Scalar.cpp new file mode 100644 index 0000000..26f7437 --- /dev/null +++ b/source/Core/Scalar.cpp @@ -0,0 +1,2279 @@ +//===-- Scalar.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Scalar.h" + +#include <math.h> +#include <inttypes.h> + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/Endian.h" + +#include "Plugins/Process/Utility/InstructionUtils.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Promote to max type currently follows the ANSI C rule for type +// promotion in expressions. +//---------------------------------------------------------------------- +static Scalar::Type +PromoteToMaxType +( + const Scalar& lhs, // The const left hand side object + const Scalar& rhs, // The const right hand side object + Scalar& temp_value, // A modifiable temp value than can be used to hold either the promoted lhs or rhs object + const Scalar* &promoted_lhs_ptr, // Pointer to the resulting possibly promoted value of lhs (at most one of lhs/rhs will get promoted) + const Scalar* &promoted_rhs_ptr // Pointer to the resulting possibly promoted value of rhs (at most one of lhs/rhs will get promoted) +) +{ + Scalar result; + // Initialize the promoted values for both the right and left hand side values + // to be the objects themselves. If no promotion is needed (both right and left + // have the same type), then the temp_value will not get used. + promoted_lhs_ptr = &lhs; + promoted_rhs_ptr = &rhs; + // Extract the types of both the right and left hand side values + Scalar::Type lhs_type = lhs.GetType(); + Scalar::Type rhs_type = rhs.GetType(); + + if (lhs_type > rhs_type) + { + // Right hand side need to be promoted + temp_value = rhs; // Copy right hand side into the temp value + if (temp_value.Promote(lhs_type)) // Promote it + promoted_rhs_ptr = &temp_value; // Update the pointer for the promoted right hand side + } + else if (lhs_type < rhs_type) + { + // Left hand side need to be promoted + temp_value = lhs; // Copy left hand side value into the temp value + if (temp_value.Promote(rhs_type)) // Promote it + promoted_lhs_ptr = &temp_value; // Update the pointer for the promoted left hand side + } + + // Make sure our type promotion worked as exptected + if (promoted_lhs_ptr->GetType() == promoted_rhs_ptr->GetType()) + return promoted_lhs_ptr->GetType(); // Return the resulting max type + + // Return the void type (zero) if we fail to promote either of the values. + return Scalar::e_void; +} + + +//---------------------------------------------------------------------- +// Scalar constructor +//---------------------------------------------------------------------- +Scalar::Scalar() : + m_type(e_void), + m_data() +{ +} + +//---------------------------------------------------------------------- +// Scalar copy constructor +//---------------------------------------------------------------------- +Scalar::Scalar(const Scalar& rhs) : + m_type(rhs.m_type), + m_data(rhs.m_data) // TODO: verify that for C++ this will correctly copy the union?? +{ +} + +//Scalar::Scalar(const RegisterValue& reg) : +// m_type(e_void), +// m_data() +//{ +// switch (reg.info.encoding) +// { +// case eEncodingUint: // unsigned integer +// switch (reg.info.byte_size) +// { +// case 1: m_type = e_uint; m_data.uint = reg.value.uint8; break; +// case 2: m_type = e_uint; m_data.uint = reg.value.uint16; break; +// case 4: m_type = e_uint; m_data.uint = reg.value.uint32; break; +// case 8: m_type = e_ulonglong; m_data.ulonglong = reg.value.uint64; break; +// break; +// } +// break; +// +// case eEncodingSint: // signed integer +// switch (reg.info.byte_size) +// { +// case 1: m_type = e_sint; m_data.sint = reg.value.sint8; break; +// case 2: m_type = e_sint; m_data.sint = reg.value.sint16; break; +// case 4: m_type = e_sint; m_data.sint = reg.value.sint32; break; +// case 8: m_type = e_slonglong; m_data.slonglong = reg.value.sint64; break; +// break; +// } +// break; +// +// case eEncodingIEEE754: // float +// switch (reg.info.byte_size) +// { +// case 4: m_type = e_float; m_data.flt = reg.value.float32; break; +// case 8: m_type = e_double; m_data.dbl = reg.value.float64; break; +// break; +// } +// break; +// case eEncodingVector: // vector registers +// break; +// } +//} + +bool +Scalar::GetData (DataExtractor &data, size_t limit_byte_size) const +{ + size_t byte_size = GetByteSize(); + if (byte_size > 0) + { + if (limit_byte_size < byte_size) + { + if (lldb::endian::InlHostByteOrder() == eByteOrderLittle) + { + // On little endian systems if we want fewer bytes from the + // current type we just specify fewer bytes since the LSByte + // is first... + data.SetData((uint8_t*)&m_data, limit_byte_size, lldb::endian::InlHostByteOrder()); + } + else if (lldb::endian::InlHostByteOrder() == eByteOrderBig) + { + // On big endian systems if we want fewer bytes from the + // current type have to advance our initial byte pointer and + // trim down the number of bytes since the MSByte is first + data.SetData(((uint8_t*)&m_data) + byte_size - limit_byte_size, limit_byte_size, lldb::endian::InlHostByteOrder()); + } + } + else + { + // We want all of the data + data.SetData((uint8_t*)&m_data, byte_size, lldb::endian::InlHostByteOrder()); + } + return true; + } + data.Clear(); + return false; +} + +size_t +Scalar::GetByteSize() const +{ + switch (m_type) + { + case e_void: + break; + case e_sint: return sizeof(m_data.sint); + case e_uint: return sizeof(m_data.uint); + case e_slong: return sizeof(m_data.slong); + case e_ulong: return sizeof(m_data.ulong); + case e_slonglong: return sizeof(m_data.slonglong); + case e_ulonglong: return sizeof(m_data.ulonglong); + case e_float: return sizeof(m_data.flt); + case e_double: return sizeof(m_data.dbl); + case e_long_double: return sizeof(m_data.ldbl); + } + return 0; +} + +bool +Scalar::IsZero() const +{ + switch (m_type) + { + case e_void: + break; + case e_sint: return m_data.sint == 0; + case e_uint: return m_data.uint == 0; + case e_slong: return m_data.slong == 0; + case e_ulong: return m_data.ulong == 0; + case e_slonglong: return m_data.slonglong == 0; + case e_ulonglong: return m_data.ulonglong == 0; + case e_float: return m_data.flt == 0.0f; + case e_double: return m_data.dbl == 0.0; + case e_long_double: return m_data.ldbl == 0.0; + } + return false; +} + +void +Scalar::GetValue (Stream *s, bool show_type) const +{ + if (show_type) + s->Printf("(%s) ", GetTypeAsCString()); + + switch (m_type) + { + case e_void: + break; + case e_sint: s->Printf("%i", m_data.sint); break; + case e_uint: s->Printf("0x%8.8x", m_data.uint); break; + case e_slong: s->Printf("%li", m_data.slong); break; + case e_ulong: s->Printf("0x%8.8lx", m_data.ulong); break; + case e_slonglong: s->Printf("%lli", m_data.slonglong); break; + case e_ulonglong: s->Printf("0x%16.16llx", m_data.ulonglong); break; + case e_float: s->Printf("%f", m_data.flt); break; + case e_double: s->Printf("%g", m_data.dbl); break; + case e_long_double: s->Printf("%Lg", m_data.ldbl); break; + } +} + +const char * +Scalar::GetTypeAsCString() const +{ + switch (m_type) + { + case e_void: return "void"; + case e_sint: return "int"; + case e_uint: return "unsigned int"; + case e_slong: return "long"; + case e_ulong: return "unsigned long"; + case e_slonglong: return "long long"; + case e_ulonglong: return "unsigned long long"; + case e_float: return "float"; + case e_double: return "double"; + case e_long_double: return "long double"; + } + return "<invalid Scalar type>"; +} + + + +//---------------------------------------------------------------------- +// Scalar copy constructor +//---------------------------------------------------------------------- +Scalar& +Scalar::operator=(const Scalar& rhs) +{ + if (this != &rhs) + { + m_type = rhs.m_type; + ::memcpy (&m_data, &rhs.m_data, sizeof(m_data)); + } + return *this; +} + +Scalar& +Scalar::operator= (const int v) +{ + m_type = e_sint; + m_data.sint = v; + return *this; +} + + +Scalar& +Scalar::operator= (unsigned int v) +{ + m_type = e_uint; + m_data.uint = v; + return *this; +} + +Scalar& +Scalar::operator= (long v) +{ + m_type = e_slong; + m_data.slong = v; + return *this; +} + +Scalar& +Scalar::operator= (unsigned long v) +{ + m_type = e_ulong; + m_data.ulong = v; + return *this; +} + +Scalar& +Scalar::operator= (long long v) +{ + m_type = e_slonglong; + m_data.slonglong = v; + return *this; +} + +Scalar& +Scalar::operator= (unsigned long long v) +{ + m_type = e_ulonglong; + m_data.ulonglong = v; + return *this; +} + +Scalar& +Scalar::operator= (float v) +{ + m_type = e_float; + m_data.flt = v; + return *this; +} + +Scalar& +Scalar::operator= (double v) +{ + m_type = e_double; + m_data.dbl = v; + return *this; +} + +Scalar& +Scalar::operator= (long double v) +{ + m_type = e_long_double; + m_data.ldbl = v; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Scalar::~Scalar() +{ +} + +bool +Scalar::Promote(Scalar::Type type) +{ + bool success = false; + switch (m_type) + { + case e_void: + break; + + case e_sint: + switch (type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_data.uint = m_data.sint; success = true; break; + case e_slong: m_data.slong = m_data.sint; success = true; break; + case e_ulong: m_data.ulong = m_data.sint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.sint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.sint; success = true; break; + case e_float: m_data.flt = m_data.sint; success = true; break; + case e_double: m_data.dbl = m_data.sint; success = true; break; + case e_long_double: m_data.ldbl = m_data.sint; success = true; break; + } + break; + + case e_uint: + switch (type) + { + case e_void: + case e_sint: break; + case e_uint: success = true; break; + case e_slong: m_data.slong = m_data.uint; success = true; break; + case e_ulong: m_data.ulong = m_data.uint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.uint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.uint; success = true; break; + case e_float: m_data.flt = m_data.uint; success = true; break; + case e_double: m_data.dbl = m_data.uint; success = true; break; + case e_long_double: m_data.ldbl = m_data.uint; success = true; break; + } + break; + + case e_slong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: break; + case e_slong: success = true; break; + case e_ulong: m_data.ulong = m_data.slong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.slong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slong; success = true; break; + case e_float: m_data.flt = m_data.slong; success = true; break; + case e_double: m_data.dbl = m_data.slong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slong; success = true; break; + } + break; + + case e_ulong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: break; + case e_ulong: success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ulong; success = true; break; + case e_float: m_data.flt = m_data.ulong; success = true; break; + case e_double: m_data.dbl = m_data.ulong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulong; success = true; break; + } + break; + + case e_slonglong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: break; + case e_slonglong: success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slonglong; success = true; break; + case e_float: m_data.flt = m_data.slonglong; success = true; break; + case e_double: m_data.dbl = m_data.slonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slonglong; success = true; break; + } + break; + + case e_ulonglong: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: break; + case e_ulonglong: success = true; break; + case e_float: m_data.flt = m_data.ulonglong; success = true; break; + case e_double: m_data.dbl = m_data.ulonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_float: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: + case e_ulonglong: break; + case e_float: success = true; break; + case e_double: m_data.dbl = m_data.flt; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_double: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: + case e_ulonglong: + case e_float: break; + case e_double: success = true; break; + case e_long_double: m_data.ldbl = m_data.dbl; success = true; break; + } + break; + + case e_long_double: + switch (type) + { + case e_void: + case e_sint: + case e_uint: + case e_slong: + case e_ulong: + case e_slonglong: + case e_ulonglong: + case e_float: + case e_double: break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +const char * +Scalar::GetValueTypeAsCString (Scalar::Type type) +{ + switch (type) + { + case e_void: return "void"; + case e_sint: return "int"; + case e_uint: return "unsigned int"; + case e_slong: return "long"; + case e_ulong: return "unsigned long"; + case e_slonglong: return "long long"; + case e_ulonglong: return "unsigned long long"; + case e_float: return "float"; + case e_double: return "double"; + case e_long_double: return "long double"; + } + return "???"; +} + + +Scalar::Type +Scalar::GetValueTypeForSignedIntegerWithByteSize (size_t byte_size) +{ + if (byte_size <= sizeof(sint_t)) + return e_sint; + if (byte_size <= sizeof(slong_t)) + return e_slong; + if (byte_size <= sizeof(slonglong_t)) + return e_slonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForUnsignedIntegerWithByteSize (size_t byte_size) +{ + if (byte_size <= sizeof(uint_t)) + return e_uint; + if (byte_size <= sizeof(ulong_t)) + return e_ulong; + if (byte_size <= sizeof(ulonglong_t)) + return e_ulonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForFloatWithByteSize (size_t byte_size) +{ + if (byte_size == sizeof(float_t)) + return e_float; + if (byte_size == sizeof(double_t)) + return e_double; + if (byte_size == sizeof(long_double_t)) + return e_long_double; + return e_void; +} + +bool +Scalar::Cast(Scalar::Type type) +{ + bool success = false; + switch (m_type) + { + case e_void: + break; + + case e_sint: + switch (type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_data.uint = m_data.sint; success = true; break; + case e_slong: m_data.slong = m_data.sint; success = true; break; + case e_ulong: m_data.ulong = m_data.sint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.sint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.sint; success = true; break; + case e_float: m_data.flt = m_data.sint; success = true; break; + case e_double: m_data.dbl = m_data.sint; success = true; break; + case e_long_double: m_data.ldbl = m_data.sint; success = true; break; + } + break; + + case e_uint: + switch (type) + { + case e_void: + case e_sint: m_data.sint = m_data.uint; success = true; break; + case e_uint: success = true; break; + case e_slong: m_data.slong = m_data.uint; success = true; break; + case e_ulong: m_data.ulong = m_data.uint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.uint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.uint; success = true; break; + case e_float: m_data.flt = m_data.uint; success = true; break; + case e_double: m_data.dbl = m_data.uint; success = true; break; + case e_long_double: m_data.ldbl = m_data.uint; success = true; break; + } + break; + + case e_slong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.slong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.slong; success = true; break; + case e_slong: success = true; break; + case e_ulong: m_data.ulong = m_data.slong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.slong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slong; success = true; break; + case e_float: m_data.flt = m_data.slong; success = true; break; + case e_double: m_data.dbl = m_data.slong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slong; success = true; break; + } + break; + + case e_ulong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ulong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ulong; success = true; break; + case e_slong: m_data.slong = m_data.ulong; success = true; break; + case e_ulong: success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ulong; success = true; break; + case e_float: m_data.flt = m_data.ulong; success = true; break; + case e_double: m_data.dbl = m_data.ulong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulong; success = true; break; + } + break; + + case e_slonglong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.slonglong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.slonglong; success = true; break; + case e_slong: m_data.slong = m_data.slonglong; success = true; break; + case e_ulong: m_data.ulong = m_data.slonglong; success = true; break; + case e_slonglong: success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slonglong; success = true; break; + case e_float: m_data.flt = m_data.slonglong; success = true; break; + case e_double: m_data.dbl = m_data.slonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slonglong; success = true; break; + } + break; + + case e_ulonglong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ulonglong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ulonglong; success = true; break; + case e_slong: m_data.slong = m_data.ulonglong; success = true; break; + case e_ulong: m_data.ulong = m_data.ulonglong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulonglong; success = true; break; + case e_ulonglong: success = true; break; + case e_float: m_data.flt = m_data.ulonglong; success = true; break; + case e_double: m_data.dbl = m_data.ulonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_float: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.flt; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.flt; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.flt; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.flt; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.flt; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.flt; success = true; break; + case e_float: success = true; break; + case e_double: m_data.dbl = m_data.flt; success = true; break; + case e_long_double: m_data.ldbl = m_data.flt; success = true; break; + } + break; + + case e_double: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.dbl; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.dbl; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.dbl; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.dbl; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.dbl; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.dbl; success = true; break; + case e_float: m_data.flt = (float_t)m_data.dbl; success = true; break; + case e_double: success = true; break; + case e_long_double: m_data.ldbl = m_data.dbl; success = true; break; + } + break; + + case e_long_double: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ldbl; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ldbl; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.ldbl; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.ldbl; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.ldbl; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.ldbl; success = true; break; + case e_float: m_data.flt = (float_t)m_data.ldbl; success = true; break; + case e_double: m_data.dbl = (double_t)m_data.ldbl; success = true; break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +bool +Scalar::MakeSigned () +{ + bool success = false; + + switch (m_type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_type = e_sint; success = true; break; + case e_slong: success = true; break; + case e_ulong: m_type = e_slong; success = true; break; + case e_slonglong: success = true; break; + case e_ulonglong: m_type = e_slonglong; success = true; break; + case e_float: success = true; break; + case e_double: success = true; break; + case e_long_double: success = true; break; + } + + return success; +} + +int +Scalar::SInt(int fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return m_data.sint; + case e_uint: return (int)m_data.uint; + case e_slong: return (int)m_data.slong; + case e_ulong: return (int)m_data.ulong; + case e_slonglong: return (int)m_data.slonglong; + case e_ulonglong: return (int)m_data.ulonglong; + case e_float: return (int)m_data.flt; + case e_double: return (int)m_data.dbl; + case e_long_double: return (int)m_data.ldbl; + } + return fail_value; +} + +unsigned int +Scalar::UInt(unsigned int fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned int)m_data.sint; + case e_uint: return (unsigned int)m_data.uint; + case e_slong: return (unsigned int)m_data.slong; + case e_ulong: return (unsigned int)m_data.ulong; + case e_slonglong: return (unsigned int)m_data.slonglong; + case e_ulonglong: return (unsigned int)m_data.ulonglong; + case e_float: return (unsigned int)m_data.flt; + case e_double: return (unsigned int)m_data.dbl; + case e_long_double: return (unsigned int)m_data.ldbl; + } + return fail_value; +} + + +long +Scalar::SLong(long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long)m_data.sint; + case e_uint: return (long)m_data.uint; + case e_slong: return (long)m_data.slong; + case e_ulong: return (long)m_data.ulong; + case e_slonglong: return (long)m_data.slonglong; + case e_ulonglong: return (long)m_data.ulonglong; + case e_float: return (long)m_data.flt; + case e_double: return (long)m_data.dbl; + case e_long_double: return (long)m_data.ldbl; + } + return fail_value; +} + + + +unsigned long +Scalar::ULong(unsigned long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned long)m_data.sint; + case e_uint: return (unsigned long)m_data.uint; + case e_slong: return (unsigned long)m_data.slong; + case e_ulong: return (unsigned long)m_data.ulong; + case e_slonglong: return (unsigned long)m_data.slonglong; + case e_ulonglong: return (unsigned long)m_data.ulonglong; + case e_float: return (unsigned long)m_data.flt; + case e_double: return (unsigned long)m_data.dbl; + case e_long_double: return (unsigned long)m_data.ldbl; + } + return fail_value; +} + +uint64_t +Scalar::GetRawBits64(uint64_t fail_value) const +{ + switch (m_type) + { + case e_void: + break; + + case e_sint: + case e_uint: + return m_data.uint; + + case e_slong: + case e_ulong: + return m_data.ulong; + + case e_slonglong: + case e_ulonglong: + return m_data.ulonglong; + + case e_float: + if (sizeof(m_data.flt) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.flt) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.flt) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.dbl) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.dbl) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.ldbl) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.ldbl) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + } + return fail_value; +} + + + +long long +Scalar::SLongLong(long long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long long)m_data.sint; + case e_uint: return (long long)m_data.uint; + case e_slong: return (long long)m_data.slong; + case e_ulong: return (long long)m_data.ulong; + case e_slonglong: return (long long)m_data.slonglong; + case e_ulonglong: return (long long)m_data.ulonglong; + case e_float: return (long long)m_data.flt; + case e_double: return (long long)m_data.dbl; + case e_long_double: return (long long)m_data.ldbl; + } + return fail_value; +} + + +unsigned long long +Scalar::ULongLong(unsigned long long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned long long)m_data.sint; + case e_uint: return (unsigned long long)m_data.uint; + case e_slong: return (unsigned long long)m_data.slong; + case e_ulong: return (unsigned long long)m_data.ulong; + case e_slonglong: return (unsigned long long)m_data.slonglong; + case e_ulonglong: return (unsigned long long)m_data.ulonglong; + case e_float: return (unsigned long long)m_data.flt; + case e_double: return (unsigned long long)m_data.dbl; + case e_long_double: return (unsigned long long)m_data.ldbl; + } + return fail_value; +} + + +float +Scalar::Float(float fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (float)m_data.sint; + case e_uint: return (float)m_data.uint; + case e_slong: return (float)m_data.slong; + case e_ulong: return (float)m_data.ulong; + case e_slonglong: return (float)m_data.slonglong; + case e_ulonglong: return (float)m_data.ulonglong; + case e_float: return (float)m_data.flt; + case e_double: return (float)m_data.dbl; + case e_long_double: return (float)m_data.ldbl; + } + return fail_value; +} + + +double +Scalar::Double(double fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (double)m_data.sint; + case e_uint: return (double)m_data.uint; + case e_slong: return (double)m_data.slong; + case e_ulong: return (double)m_data.ulong; + case e_slonglong: return (double)m_data.slonglong; + case e_ulonglong: return (double)m_data.ulonglong; + case e_float: return (double)m_data.flt; + case e_double: return (double)m_data.dbl; + case e_long_double: return (double)m_data.ldbl; + } + return fail_value; +} + + +long double +Scalar::LongDouble(long double fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long double)m_data.sint; + case e_uint: return (long double)m_data.uint; + case e_slong: return (long double)m_data.slong; + case e_ulong: return (long double)m_data.ulong; + case e_slonglong: return (long double)m_data.slonglong; + case e_ulonglong: return (long double)m_data.ulonglong; + case e_float: return (long double)m_data.flt; + case e_double: return (long double)m_data.dbl; + case e_long_double: return (long double)m_data.ldbl; + } + return fail_value; +} + + +Scalar& +Scalar::operator+= (const Scalar& rhs) +{ + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((m_type = PromoteToMaxType(*this, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (m_type) + { + case e_void: break; + case e_sint: m_data.sint = a->m_data.sint + b->m_data.sint; break; + case e_uint: m_data.uint = a->m_data.uint + b->m_data.uint; break; + case e_slong: m_data.slong = a->m_data.slong + b->m_data.slong; break; + case e_ulong: m_data.ulong = a->m_data.ulong + b->m_data.ulong; break; + case e_slonglong: m_data.slonglong = a->m_data.slonglong + b->m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong = a->m_data.ulonglong + b->m_data.ulonglong; break; + case e_float: m_data.flt = a->m_data.flt + b->m_data.flt; break; + case e_double: m_data.dbl = a->m_data.dbl + b->m_data.dbl; break; + case e_long_double: m_data.ldbl = a->m_data.ldbl + b->m_data.ldbl; break; + } + } + return *this; +} + +Scalar& +Scalar::operator<<= (const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.sint <<= rhs.m_data.sint; break; + case e_uint: m_data.sint <<= rhs.m_data.uint; break; + case e_slong: m_data.sint <<= rhs.m_data.slong; break; + case e_ulong: m_data.sint <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint <<= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint <<= rhs.m_data.sint; break; + case e_uint: m_data.uint <<= rhs.m_data.uint; break; + case e_slong: m_data.uint <<= rhs.m_data.slong; break; + case e_ulong: m_data.uint <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint <<= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slong <<= rhs.m_data.sint; break; + case e_uint: m_data.slong <<= rhs.m_data.uint; break; + case e_slong: m_data.slong <<= rhs.m_data.slong; break; + case e_ulong: m_data.slong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong <<= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong <<= rhs.m_data.sint; break; + case e_uint: m_data.ulong <<= rhs.m_data.uint; break; + case e_slong: m_data.ulong <<= rhs.m_data.slong; break; + case e_ulong: m_data.ulong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong <<= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slonglong <<= rhs.m_data.sint; break; + case e_uint: m_data.slonglong <<= rhs.m_data.uint; break; + case e_slong: m_data.slonglong <<= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong <<= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong <<= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong <<= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong <<= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong <<= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + +bool +Scalar::ShiftRightLogical(const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint >>= rhs.m_data.sint; break; + case e_uint: m_data.uint >>= rhs.m_data.uint; break; + case e_slong: m_data.uint >>= rhs.m_data.slong; break; + case e_ulong: m_data.uint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slonglong: + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong >>= rhs.m_data.ulonglong; break; + } + break; + } + return m_type != e_void; +} + + +Scalar& +Scalar::operator>>= (const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.sint >>= rhs.m_data.sint; break; + case e_uint: m_data.sint >>= rhs.m_data.uint; break; + case e_slong: m_data.sint >>= rhs.m_data.slong; break; + case e_ulong: m_data.sint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint >>= rhs.m_data.sint; break; + case e_uint: m_data.uint >>= rhs.m_data.uint; break; + case e_slong: m_data.uint >>= rhs.m_data.slong; break; + case e_ulong: m_data.uint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slong >>= rhs.m_data.sint; break; + case e_uint: m_data.slong >>= rhs.m_data.uint; break; + case e_slong: m_data.slong >>= rhs.m_data.slong; break; + case e_ulong: m_data.slong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong >>= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.slonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.slonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong >>= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + + +Scalar& +Scalar::operator&= (const Scalar& rhs) +{ + switch (m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + + case e_sint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.sint &= rhs.m_data.sint; break; + case e_uint: m_data.sint &= rhs.m_data.uint; break; + case e_slong: m_data.sint &= rhs.m_data.slong; break; + case e_ulong: m_data.sint &= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint &= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.uint &= rhs.m_data.sint; break; + case e_uint: m_data.uint &= rhs.m_data.uint; break; + case e_slong: m_data.uint &= rhs.m_data.slong; break; + case e_ulong: m_data.uint &= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint &= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slong &= rhs.m_data.sint; break; + case e_uint: m_data.slong &= rhs.m_data.uint; break; + case e_slong: m_data.slong &= rhs.m_data.slong; break; + case e_ulong: m_data.slong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong &= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulong &= rhs.m_data.sint; break; + case e_uint: m_data.ulong &= rhs.m_data.uint; break; + case e_slong: m_data.ulong &= rhs.m_data.slong; break; + case e_ulong: m_data.ulong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong &= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.slonglong &= rhs.m_data.sint; break; + case e_uint: m_data.slonglong &= rhs.m_data.uint; break; + case e_slong: m_data.slonglong &= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong &= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + switch (rhs.m_type) + { + case e_void: + case e_float: + case e_double: + case e_long_double: + m_type = e_void; + break; + case e_sint: m_data.ulonglong &= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong &= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong &= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong &= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + + + +bool +Scalar::AbsoluteValue() +{ + switch (m_type) + { + case e_void: + break; + + case e_sint: + if (m_data.sint < 0) + m_data.sint = -m_data.sint; + return true; + + case e_slong: + if (m_data.slong < 0) + m_data.slong = -m_data.slong; + return true; + + case e_slonglong: + if (m_data.slonglong < 0) + m_data.slonglong = -m_data.slonglong; + return true; + + case e_uint: + case e_ulong: + case e_ulonglong: return true; + case e_float: m_data.flt = fabsf(m_data.flt); return true; + case e_double: m_data.dbl = fabs(m_data.dbl); return true; + case e_long_double: m_data.ldbl = fabsl(m_data.ldbl); return true; + } + return false; +} + + +bool +Scalar::UnaryNegate() +{ + switch (m_type) + { + case e_void: break; + case e_sint: m_data.sint = -m_data.sint; return true; + case e_uint: m_data.uint = -m_data.uint; return true; + case e_slong: m_data.slong = -m_data.slong; return true; + case e_ulong: m_data.ulong = -m_data.ulong; return true; + case e_slonglong: m_data.slonglong = -m_data.slonglong; return true; + case e_ulonglong: m_data.ulonglong = -m_data.ulonglong; return true; + case e_float: m_data.flt = -m_data.flt; return true; + case e_double: m_data.dbl = -m_data.dbl; return true; + case e_long_double: m_data.ldbl = -m_data.ldbl; return true; + } + return false; +} + +bool +Scalar::OnesComplement() +{ + switch (m_type) + { + case e_sint: m_data.sint = ~m_data.sint; return true; + case e_uint: m_data.uint = ~m_data.uint; return true; + case e_slong: m_data.slong = ~m_data.slong; return true; + case e_ulong: m_data.ulong = ~m_data.ulong; return true; + case e_slonglong: m_data.slonglong = ~m_data.slonglong; return true; + case e_ulonglong: m_data.ulonglong = ~m_data.ulonglong; return true; + + case e_void: + case e_float: + case e_double: + case e_long_double: + break; + } + return false; +} + + +const Scalar +lldb_private::operator+ (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + case Scalar::e_sint: result.m_data.sint = a->m_data.sint + b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint + b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong + b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong + b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong + b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong + b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt + b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl + b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl + b->m_data.ldbl; break; + } + } + return result; +} + + +const Scalar +lldb_private::operator- (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + case Scalar::e_sint: result.m_data.sint = a->m_data.sint - b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint - b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong - b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong - b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong - b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong - b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt - b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl - b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl - b->m_data.ldbl; break; + } + } + return result; +} + +const Scalar +lldb_private::operator/ (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + + case Scalar::e_sint: if (b->m_data.sint != 0) { result.m_data.sint = a->m_data.sint/ b->m_data.sint; return result; } break; + case Scalar::e_uint: if (b->m_data.uint != 0) { result.m_data.uint = a->m_data.uint / b->m_data.uint; return result; } break; + case Scalar::e_slong: if (b->m_data.slong != 0) { result.m_data.slong = a->m_data.slong / b->m_data.slong; return result; } break; + case Scalar::e_ulong: if (b->m_data.ulong != 0) { result.m_data.ulong = a->m_data.ulong / b->m_data.ulong; return result; } break; + case Scalar::e_slonglong: if (b->m_data.slonglong != 0) { result.m_data.slonglong = a->m_data.slonglong / b->m_data.slonglong; return result; } break; + case Scalar::e_ulonglong: if (b->m_data.ulonglong != 0) { result.m_data.ulonglong = a->m_data.ulonglong / b->m_data.ulonglong; return result; } break; + case Scalar::e_float: if (b->m_data.flt != 0.0f) { result.m_data.flt = a->m_data.flt / b->m_data.flt; return result; } break; + case Scalar::e_double: if (b->m_data.dbl != 0.0) { result.m_data.dbl = a->m_data.dbl / b->m_data.dbl; return result; } break; + case Scalar::e_long_double: if (b->m_data.ldbl != 0.0) { result.m_data.ldbl = a->m_data.ldbl / b->m_data.ldbl; return result; } break; + } + } + // For division only, the only way it should make it here is if a promotion failed, + // or if we are trying to do a divide by zero. + result.m_type = Scalar::e_void; + return result; +} + +const Scalar +lldb_private::operator* (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_void: break; + case Scalar::e_sint: result.m_data.sint = a->m_data.sint * b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint * b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong * b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong * b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong * b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong * b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt * b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl * b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl * b->m_data.ldbl; break; + } + } + return result; +} + +const Scalar +lldb_private::operator& (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_sint: result.m_data.sint = a->m_data.sint & b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint & b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong & b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong & b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong & b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong & b->m_data.ulonglong; break; + + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + // No bitwise AND on floats, doubles of long doubles + result.m_type = Scalar::e_void; + break; + } + } + return result; +} + +const Scalar +lldb_private::operator| (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_sint: result.m_data.sint = a->m_data.sint | b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint | b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong | b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong | b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong | b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong | b->m_data.ulonglong; break; + + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + // No bitwise AND on floats, doubles of long doubles + result.m_type = Scalar::e_void; + break; + } + } + return result; +} + +const Scalar +lldb_private::operator% (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + default: break; + case Scalar::e_sint: if (b->m_data.sint != 0) { result.m_data.sint = a->m_data.sint % b->m_data.sint; return result; } break; + case Scalar::e_uint: if (b->m_data.uint != 0) { result.m_data.uint = a->m_data.uint % b->m_data.uint; return result; } break; + case Scalar::e_slong: if (b->m_data.slong != 0) { result.m_data.slong = a->m_data.slong % b->m_data.slong; return result; } break; + case Scalar::e_ulong: if (b->m_data.ulong != 0) { result.m_data.ulong = a->m_data.ulong % b->m_data.ulong; return result; } break; + case Scalar::e_slonglong: if (b->m_data.slonglong != 0) { result.m_data.slonglong = a->m_data.slonglong % b->m_data.slonglong; return result; } break; + case Scalar::e_ulonglong: if (b->m_data.ulonglong != 0) { result.m_data.ulonglong = a->m_data.ulonglong % b->m_data.ulonglong; return result; } break; + } + } + result.m_type = Scalar::e_void; + return result; +} + +const Scalar +lldb_private::operator^ (const Scalar& lhs, const Scalar& rhs) +{ + Scalar result; + Scalar temp_value; + const Scalar* a; + const Scalar* b; + if ((result.m_type = PromoteToMaxType(lhs, rhs, temp_value, a, b)) != Scalar::e_void) + { + switch (result.m_type) + { + case Scalar::e_sint: result.m_data.sint = a->m_data.sint ^ b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint ^ b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong ^ b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong ^ b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong ^ b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong ^ b->m_data.ulonglong; break; + + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + // No bitwise AND on floats, doubles of long doubles + result.m_type = Scalar::e_void; + break; + } + } + return result; +} + +const Scalar +lldb_private::operator<< (const Scalar& lhs, const Scalar &rhs) +{ + Scalar result = lhs; + result <<= rhs; + return result; +} + +const Scalar +lldb_private::operator>> (const Scalar& lhs, const Scalar &rhs) +{ + Scalar result = lhs; + result >>= rhs; + return result; +} + +// Return the raw unsigned integer without any casting or conversion +unsigned int +Scalar::RawUInt () const +{ + return m_data.uint; +} + +// Return the raw unsigned long without any casting or conversion +unsigned long +Scalar::RawULong () const +{ + return m_data.ulong; +} + +// Return the raw unsigned long long without any casting or conversion +unsigned long long +Scalar::RawULongLong () const +{ + return m_data.ulonglong; +} + + +Error +Scalar::SetValueFromCString (const char *value_str, Encoding encoding, size_t byte_size) +{ + Error error; + if (value_str == NULL || value_str[0] == '\0') + { + error.SetErrorString ("Invalid c-string value string."); + return error; + } + bool success = false; + switch (encoding) + { + case eEncodingInvalid: + error.SetErrorString ("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size <= sizeof (unsigned long long)) + { + uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); + else if (!UIntValueIsValidForSize (uval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %zu byte unsigned integer value", uval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_uint: m_data.uint = (uint_t)uval64; break; + case e_ulong: m_data.ulong = (ulong_t)uval64; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)uval64; break; + default: + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingSint: + if (byte_size <= sizeof (long long)) + { + uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); + else if (!SIntValueIsValidForSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %zu byte signed integer value", sval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_sint: m_data.sint = (sint_t)sval64; break; + case e_slong: m_data.slong = (slong_t)sval64; break; + case e_slonglong: m_data.slonglong = (slonglong_t)sval64; break; + default: + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingIEEE754: + if (byte_size == sizeof (float)) + { + if (::sscanf (value_str, "%f", &m_data.flt) == 1) + m_type = e_float; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (double)) + { + if (::sscanf (value_str, "%lf", &m_data.dbl) == 1) + m_type = e_double; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (long double)) + { + if (::sscanf (value_str, "%Lf", &m_data.ldbl) == 1) + m_type = e_long_double; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else + { + error.SetErrorStringWithFormat ("unsupported float byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingVector: + error.SetErrorString ("vector encoding unsupported."); + break; + } + if (error.Fail()) + m_type = e_void; + + return error; +} + +Error +Scalar::SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t byte_size) +{ + Error error; + + switch (encoding) + { + case lldb::eEncodingInvalid: + error.SetErrorString ("invalid encoding"); + break; + case lldb::eEncodingVector: + error.SetErrorString ("vector encoding unsupported"); + break; + case lldb::eEncodingUint: + { + lldb::offset_t offset; + + switch (byte_size) + { + case 1: operator=((uint8_t)data.GetU8(&offset)); break; + case 2: operator=((uint16_t)data.GetU16(&offset)); break; + case 4: operator=((uint32_t)data.GetU32(&offset)); break; + case 8: operator=((uint64_t)data.GetU64(&offset)); break; + default: + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + break; + } + } + break; + case lldb::eEncodingSint: + { + lldb::offset_t offset; + + switch (byte_size) + { + case 1: operator=((int8_t)data.GetU8(&offset)); break; + case 2: operator=((int16_t)data.GetU16(&offset)); break; + case 4: operator=((int32_t)data.GetU32(&offset)); break; + case 8: operator=((int64_t)data.GetU64(&offset)); break; + default: + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + break; + } + } + break; + case lldb::eEncodingIEEE754: + { + lldb::offset_t offset; + + if (byte_size == sizeof (float)) + operator=((float)data.GetFloat(&offset)); + else if (byte_size == sizeof (double)) + operator=((double)data.GetDouble(&offset)); + else if (byte_size == sizeof (long double)) + operator=((long double)data.GetLongDouble(&offset)); + else + error.SetErrorStringWithFormat ("unsupported float byte size: %zu", byte_size); + } + break; + } + + return error; +} + +bool +Scalar::SignExtend (uint32_t sign_bit_pos) +{ + const uint32_t max_bit_pos = GetByteSize() * 8; + + if (sign_bit_pos < max_bit_pos) + { + switch (m_type) + { + case Scalar::e_void: + case Scalar::e_float: + case Scalar::e_double: + case Scalar::e_long_double: + return false; + + case Scalar::e_sint: + case Scalar::e_uint: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned int sign_bit = 1u << sign_bit_pos; + if (m_data.uint & sign_bit) + { + const unsigned int mask = ~(sign_bit) + 1u; + m_data.uint |= mask; + } + return true; + } + break; + + case Scalar::e_slong: + case Scalar::e_ulong: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned long sign_bit = 1ul << sign_bit_pos; + if (m_data.ulong & sign_bit) + { + const unsigned long mask = ~(sign_bit) + 1ul; + m_data.ulong |= mask; + } + return true; + } + break; + + case Scalar::e_slonglong: + case Scalar::e_ulonglong: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned long long sign_bit = 1ull << sign_bit_pos; + if (m_data.ulonglong & sign_bit) + { + const unsigned long long mask = ~(sign_bit) + 1ull; + m_data.ulonglong |= mask; + } + return true; + } + break; + } + } + return false; +} + +size_t +Scalar::GetAsMemoryData (void *dst, + size_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const +{ + // Get a data extractor that points to the native scalar data + DataExtractor data; + if (!GetData(data)) + { + error.SetErrorString ("invalid scalar value"); + return 0; + } + + const size_t src_len = data.GetByteSize(); + + // Prepare a memory buffer that contains some or all of the register value + const size_t bytes_copied = data.CopyByteOrderedData (0, // src offset + src_len, // src length + dst, // dst buffer + dst_len, // dst length + dst_byte_order); // dst byte order + if (bytes_copied == 0) + error.SetErrorString ("failed to copy data"); + + return bytes_copied; +} + +bool +Scalar::ExtractBitfield (uint32_t bit_size, + uint32_t bit_offset) +{ + if (bit_size == 0) + return true; + + uint32_t msbit = bit_offset + bit_size - 1; + uint32_t lsbit = bit_offset; + switch (m_type) + { + case Scalar::e_void: + break; + + case e_float: + if (sizeof(m_data.flt) == sizeof(sint_t)) + m_data.sint = (sint_t)SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.flt) == sizeof(ulong_t)) + m_data.slong = (slong_t)SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.flt) == sizeof(ulonglong_t)) + m_data.slonglong = (slonglong_t)SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(sint_t)) + m_data.sint = SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.dbl) == sizeof(ulong_t)) + m_data.slong = SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.dbl) == sizeof(ulonglong_t)) + m_data.slonglong = SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(sint_t)) + m_data.sint = SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.ldbl) == sizeof(ulong_t)) + m_data.slong = SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.ldbl) == sizeof(ulonglong_t)) + m_data.slonglong = SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case Scalar::e_sint: + m_data.sint = (sint_t)SignedBits (m_data.sint, msbit, lsbit); + return true; + + case Scalar::e_uint: + m_data.uint = (uint_t)UnsignedBits (m_data.uint, msbit, lsbit); + return true; + + case Scalar::e_slong: + m_data.slong = (slong_t)SignedBits (m_data.slong, msbit, lsbit); + return true; + + case Scalar::e_ulong: + m_data.ulong = (ulong_t)UnsignedBits (m_data.ulong, msbit, lsbit); + return true; + + case Scalar::e_slonglong: + m_data.slonglong = (slonglong_t)SignedBits (m_data.slonglong, msbit, lsbit); + return true; + + case Scalar::e_ulonglong: + m_data.ulonglong = (ulonglong_t)UnsignedBits (m_data.ulonglong, msbit, lsbit); + return true; + } + return false; +} + + + + + +bool +lldb_private::operator== (const Scalar& lhs, const Scalar& rhs) +{ + // If either entry is void then we can just compare the types + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return lhs.m_type == rhs.m_type; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint == b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint == b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong == b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong == b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong == b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong == b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt == b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl == b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl == b->m_data.ldbl; + } + return false; +} + +bool +lldb_private::operator!= (const Scalar& lhs, const Scalar& rhs) +{ + // If either entry is void then we can just compare the types + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return lhs.m_type != rhs.m_type; + + Scalar temp_value; // A temp value that might get a copy of either promoted value + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint != b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint != b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong != b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong != b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong != b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong != b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt != b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl != b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl != b->m_data.ldbl; + } + return true; +} + +bool +lldb_private::operator< (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint < b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint < b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong < b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong < b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong < b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong < b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt < b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl < b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl < b->m_data.ldbl; + } + return false; +} + +bool +lldb_private::operator<= (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint <= b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint <= b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong <= b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong <= b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong <= b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong <= b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt <= b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl <= b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl <= b->m_data.ldbl; + } + return false; +} + + +bool +lldb_private::operator> (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint > b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint > b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong > b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong > b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong > b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong > b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt > b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl > b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl > b->m_data.ldbl; + } + return false; +} + +bool +lldb_private::operator>= (const Scalar& lhs, const Scalar& rhs) +{ + if (lhs.m_type == Scalar::e_void || rhs.m_type == Scalar::e_void) + return false; + + Scalar temp_value; + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint >= b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint >= b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong >= b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong >= b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong >= b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong >= b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt >= b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl >= b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl >= b->m_data.ldbl; + } + return false; +} + + + + diff --git a/source/Core/SearchFilter.cpp b/source/Core/SearchFilter.cpp new file mode 100644 index 0000000..54937c0 --- /dev/null +++ b/source/Core/SearchFilter.cpp @@ -0,0 +1,816 @@ +//===-- SearchFilter.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SearchFilter constructor +//---------------------------------------------------------------------- +Searcher::Searcher () +{ + +} + +Searcher::~Searcher () +{ + +} + +void +Searcher::GetDescription (Stream *s) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter constructor +//---------------------------------------------------------------------- +SearchFilter::SearchFilter(const TargetSP &target_sp) : + m_target_sp (target_sp) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter copy constructor +//---------------------------------------------------------------------- +SearchFilter::SearchFilter(const SearchFilter& rhs) : + m_target_sp (rhs.m_target_sp) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter assignment operator +//---------------------------------------------------------------------- +const SearchFilter& +SearchFilter::operator=(const SearchFilter& rhs) +{ + m_target_sp = rhs.m_target_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilter::~SearchFilter() +{ +} + +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; +} + +uint32_t +SearchFilter::GetFilterRequiredItems() +{ + return (lldb::SymbolContextItem) 0; +} + +void +SearchFilter::GetDescription (Stream *s) +{ +} + +void +SearchFilter::Dump (Stream *s) const +{ + +} + +//---------------------------------------------------------------------- +// 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() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + 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() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + else + { + Mutex::Locker modules_locker(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 () >= Searcher::eDepthModule) + { + if (context.module_sp) + { + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(context.module_sp.get()); + searcher.SearchCallback (*this, matchingContext, NULL, false); + } + else + { + return DoCUIteration(context.module_sp, context, searcher); + } + } + else + { + const ModuleList &target_images = m_target_sp->GetImages(); + Mutex::Locker modules_locker(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 () == Searcher::eDepthModule) + { + SymbolContext matchingContext(m_target_sp, module_sp); + + Searcher::CallbackReturn shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + 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 == NULL) + { + 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 () == Searcher::eDepthCompUnit) + { + SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get()); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + + if (shouldContinue == Searcher::eCallbackReturnPop) + return Searcher::eCallbackReturnContinue; + else if (shouldContinue == Searcher::eCallbackReturnStop) + return shouldContinue; + } + else + { + // FIXME Descend to block. + } + } + } + } + else + { + if (CompUnitPasses(*context.comp_unit)) + { + SymbolContext matchingContext (m_target_sp, module_sp, context.comp_unit); + return searcher.SearchCallback (*this, matchingContext, NULL, false); + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::CallbackReturn +SearchFilter::DoFunctionIteration (Function *function, const SymbolContext &context, Searcher &searcher) +{ + // FIXME: Implement... + return Searcher::eCallbackReturnContinue; +} + +//---------------------------------------------------------------------- +// SearchFilterForNonModuleSpecificSearches: +// Selects a shared library matching a given file spec, consulting the targets "black list". +//---------------------------------------------------------------------- + + bool + SearchFilterForNonModuleSpecificSearches::ModulePasses (const FileSpec &module_spec) + { + if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_spec)) + return false; + else + return true; + } + + bool + SearchFilterForNonModuleSpecificSearches::ModulePasses (const lldb::ModuleSP &module_sp) + { + if (!module_sp) + return true; + else if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_sp)) + return false; + else + return true; + } + +//---------------------------------------------------------------------- +// SearchFilterByModule: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModule constructors +//---------------------------------------------------------------------- + +SearchFilterByModule::SearchFilterByModule (const lldb::TargetSP &target_sp, const FileSpec &module) : + SearchFilter (target_sp), + m_module_spec (module) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModule copy constructor +//---------------------------------------------------------------------- +SearchFilterByModule::SearchFilterByModule(const SearchFilterByModule& rhs) : + SearchFilter (rhs), + m_module_spec (rhs.m_module_spec) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModule assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModule& +SearchFilterByModule::operator=(const SearchFilterByModule& rhs) +{ + m_target_sp = rhs.m_target_sp; + m_module_spec = rhs.m_module_spec; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModule::~SearchFilterByModule() +{ +} + +bool +SearchFilterByModule::ModulePasses (const ModuleSP &module_sp) +{ + if (module_sp && FileSpec::Equal(module_sp->GetFileSpec(), m_module_spec, false)) + return true; + else + return false; +} + +bool +SearchFilterByModule::ModulePasses (const FileSpec &spec) +{ + // Do a full match only if "spec" has a directory + const bool full_match = 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() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, false); + } + + // 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(); + Mutex::Locker modules_locker (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 = 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 = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec.GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec.GetFilename().AsCString("<unknown>")); + } +} + +uint32_t +SearchFilterByModule::GetFilterRequiredItems() +{ + return eSymbolContextModule; +} + +void +SearchFilterByModule::Dump (Stream *s) const +{ + +} +//---------------------------------------------------------------------- +// SearchFilterByModuleList: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModuleList constructors +//---------------------------------------------------------------------- + +SearchFilterByModuleList::SearchFilterByModuleList (const lldb::TargetSP &target_sp, const FileSpecList &module_list) : + SearchFilter (target_sp), + m_module_spec_list (module_list) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModuleList copy constructor +//---------------------------------------------------------------------- +SearchFilterByModuleList::SearchFilterByModuleList(const SearchFilterByModuleList& rhs) : + SearchFilter (rhs), + m_module_spec_list (rhs.m_module_spec_list) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleList assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModuleList& +SearchFilterByModuleList::operator=(const SearchFilterByModuleList& rhs) +{ + m_target_sp = rhs.m_target_sp; + m_module_spec_list = rhs.m_module_spec_list; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModuleList::~SearchFilterByModuleList() +{ +} + +bool +SearchFilterByModuleList::ModulePasses (const ModuleSP &module_sp) +{ + if (m_module_spec_list.GetSize() == 0) + return true; + + if (module_sp && m_module_spec_list.FindFileIndex(0, module_sp->GetFileSpec(), false) != UINT32_MAX) + return true; + else + return false; +} + +bool +SearchFilterByModuleList::ModulePasses (const FileSpec &spec) +{ + if (m_module_spec_list.GetSize() == 0) + return true; + + if (m_module_spec_list.FindFileIndex(0, spec, true) != UINT32_MAX) + return true; + else + return false; +} + +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() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, false); + } + + // 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(); + Mutex::Locker modules_locker (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 = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(0).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("<unknown>")); + } + } + else + { + s->Printf (", modules(%zu) = ", num_modules); + for (size_t i = 0; i < num_modules; i++) + { + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(i).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + 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 +{ + +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU constructors +//---------------------------------------------------------------------- + +SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU (const lldb::TargetSP &target_sp, + const FileSpecList &module_list, + const FileSpecList &cu_list) : + SearchFilterByModuleList (target_sp, module_list), + m_cu_spec_list (cu_list) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU copy constructor +//---------------------------------------------------------------------- +SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(const SearchFilterByModuleListAndCU& rhs) : + SearchFilterByModuleList (rhs), + m_cu_spec_list (rhs.m_cu_spec_list) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU assignment operator +//---------------------------------------------------------------------- +const 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; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() +{ +} + +bool +SearchFilterByModuleListAndCU::AddressPasses (Address &address) +{ + return true; +} + + +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() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, false); + } + + // 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(); + Mutex::Locker modules_locker(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() == Searcher::eDepthModule) + { + 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 = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(0).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("<unknown>")); + } + } + else if (num_modules > 0) + { + s->Printf (", modules(%zd) = ", num_modules); + for (size_t i = 0; i < num_modules; i++) + { + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(i).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + 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 +{ + +} + diff --git a/source/Core/Section.cpp b/source/Core/Section.cpp new file mode 100644 index 0000000..e2a084c --- /dev/null +++ b/source/Core/Section.cpp @@ -0,0 +1,562 @@ +//===-- Section.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Section.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +Section::Section (const ModuleSP &module_sp, + ObjectFile *obj_file, + user_id_t sect_id, + const 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 flags) : + 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_children (), + m_fake (false), + m_encrypted (false), + m_thread_specific (false) +{ +// 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, + const 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 flags) : + 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_children (), + m_fake (false), + m_encrypted (false), + m_thread_specific (false) +{ +// 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(); + } + else + { + 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) const +{ + const size_t num_children = m_children.GetSize(); + if (num_children > 0) + { + 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()) + return child_section->ResolveContainedAddress (offset - child_offset, so_addr); + } + } + 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) + { + if (file_addr <= vm_addr) + { + const addr_t offset = vm_addr - file_addr; + 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(), GetSectionTypeAsCString (m_type)); + 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 0x%8.8" PRIx64 " 0x%8.8" PRIx64 " 0x%8.8x ", resolved ? ' ' : '*', 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 = NULL; + ModuleSP module_sp (GetModule()); + const FileSpec &file_spec = m_obj_file->GetFileSpec(); + + if (m_obj_file) + 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; +} + +#pragma mark SectionList + +SectionList::SectionList () : + m_sections() +{ +} + + +SectionList::~SectionList () +{ +} + +SectionList & +SectionList::operator = (const SectionList& rhs) +{ + if (this != &rhs) + m_sections = rhs.m_sections; + return *this; +} + +size_t +SectionList::AddSection (const lldb::SectionSP& section_sp) +{ + assert (section_sp.get()); + size_t section_index = m_sections.size(); + m_sections.push_back(section_sp); + return section_index; +} + +// Warning, this can be slow as it's removing items from a std::vector. +bool +SectionList::DeleteSection (size_t idx) +{ + if (idx < m_sections.size()) + { + m_sections.erase (m_sections.begin() + idx); + return true; + } + return false; +} + +size_t +SectionList::FindSectionIndex (const Section* sect) +{ + iterator sect_iter; + iterator begin = m_sections.begin(); + iterator end = m_sections.end(); + for (sect_iter = begin; sect_iter != end; ++sect_iter) + { + if (sect_iter->get() == sect) + { + // The secton was already in this section list + return std::distance (begin, sect_iter); + } + } + return UINT32_MAX; +} + +size_t +SectionList::AddUniqueSection (const lldb::SectionSP& sect_sp) +{ + size_t sect_idx = FindSectionIndex (sect_sp.get()); + if (sect_idx == UINT32_MAX) + { + sect_idx = AddSection (sect_sp); + } + return sect_idx; +} + +bool +SectionList::ReplaceSection (user_id_t sect_id, const lldb::SectionSP& sect_sp, uint32_t depth) +{ + iterator sect_iter, end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) + { + if ((*sect_iter)->GetID() == sect_id) + { + *sect_iter = sect_sp; + return true; + } + else if (depth > 0) + { + if ((*sect_iter)->GetChildren().ReplaceSection(sect_id, sect_sp, depth - 1)) + return true; + } + } + return false; +} + +size_t +SectionList::GetNumSections (uint32_t depth) const +{ + size_t count = m_sections.size(); + if (depth > 0) + { + const_iterator sect_iter, end = m_sections.end(); + for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) + { + count += (*sect_iter)->GetChildren().GetNumSections(depth - 1); + } + } + return count; +} + +SectionSP +SectionList::GetSectionAtIndex (size_t idx) const +{ + SectionSP sect_sp; + if (idx < m_sections.size()) + sect_sp = m_sections[idx]; + return sect_sp; +} + +SectionSP +SectionList::FindSectionByName (const ConstString §ion_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() == NULL; ++sect_iter) + { + Section *child_section = sect_iter->get(); + assert (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() == NULL; ++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() == NULL; ++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() == NULL && !sect->IsFake()) + sect_sp = *sect_iter; + } + } + return sect_sp; +} + +bool +SectionList::ContainsSection(user_id_t sect_id) const +{ + return FindSectionByID (sect_id).get() != NULL; +} + +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 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 : NULL, 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/source/Core/SourceManager.cpp b/source/Core/SourceManager.cpp new file mode 100644 index 0000000..9f28934 --- /dev/null +++ b/source/Core/SourceManager.cpp @@ -0,0 +1,651 @@ +//===-- SourceManager.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/SourceManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" + +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(); + + // If file_sp is no good or it points to a non-existent file, reset it. + if (!file_sp || !file_sp->GetFileSpec().Exists()) + { + file_sp.reset (new File (file_spec, target_sp.get())); + + if (debugger_sp) + debugger_sp->GetSourceFileCache().AddSourceFile(file_sp); + } + return file_sp; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile (uint32_t start_line, + uint32_t count, + uint32_t curr_line, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs) +{ + if (count == 0) + return 0; + size_t return_value = 0; + 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), " "); + } + + return_value += s->Printf("%s%2.2s %-4u\t", + prefix, + line == curr_line ? current_line_cstr : "", + line); + size_t this_line_size = m_last_file_sp->DisplaySourceLines (line, 0, 0, s); + if (this_line_size == 0) + { + m_last_line = UINT32_MAX; + break; + } + else + return_value += this_line_size; + } + } + return return_value; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbers +( + const FileSpec &file_spec, + uint32_t line, + 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, 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; + + return DisplaySourceLinesWithLineNumbersUsingLastFile (m_last_line, m_last_count, UINT32_MAX, "", 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; + bool append = false; + size_t num_matches = executable_ptr->FindFunctions (main_name, + NULL, + lldb::eFunctionNameTypeBase, + inlines_okay, + symbols_okay, + append, + sc_list); + for (size_t idx = 0; idx < num_matches; idx++) + { + SymbolContext sc; + sc_list.GetContextAtIndex(idx, sc); + if (sc.function) + { + lldb_private::LineEntry line_entry; + if (sc.function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry (line_entry)) + { + SetDefaultFileAndLine (line_entry.file, + line_entry.line); + file_spec = m_last_file_sp->GetFileSpec(); + line = m_last_line; + return true; + } + } + } + } + } + } + return false; +} + +void +SourceManager::FindLinesMatchingRegex (FileSpec &file_spec, + RegularExpression& regex, + uint32_t start_line, + uint32_t end_line, + std::vector<uint32_t> &match_lines) +{ + match_lines.clear(); + FileSP file_sp = GetFile (file_spec); + if (!file_sp) + return; + return file_sp->FindLinesMatchingRegex (regex, start_line, end_line, match_lines); +} + +SourceManager::File::File(const FileSpec &file_spec, Target *target) : + m_file_spec_orig (file_spec), + m_file_spec(file_spec), + m_mod_time (file_spec.GetModificationTime()), + m_source_map_mod_id (0), + m_data_sp(), + m_offsets() +{ + if (!m_mod_time.IsValid()) + { + 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, + lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit, + sc_list); + bool got_multiple = false; + if (num_matches != 0) + { + if (num_matches > 1) + { + SymbolContext sc; + FileSpec *test_cu_spec = NULL; + + 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 = m_file_spec.GetModificationTime(); + } + } + } + // Try remapping if m_file_spec does not correspond to an existing file. + if (!m_file_spec.Exists()) + { + 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 = m_file_spec.GetModificationTime(); + } + } + } + } + + if (m_mod_time.IsValid()) + m_data_sp = m_file_spec.ReadFileContents (); +} + +SourceManager::File::~File() +{ +} + +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; +} + +bool +SourceManager::File::LineIsValid (uint32_t line) +{ + if (line == 0) + return false; + + if (CalculateLineOffsets (line)) + return line < m_offsets.size(); + return false; +} + +size_t +SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s) +{ + // 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. + TimeValue curr_mod_time (m_file_spec.GetModificationTime()); + + if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time) + { + m_mod_time = curr_mod_time; + m_data_sp = m_file_spec.ReadFileContents (); + m_offsets.clear(); + } + + // Sanity check m_data_sp before proceeding. + if (!m_data_sp) + return 0; + + 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); + size_t bytes_written = 0; + 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; + bytes_written = s->Write(cstr, count); + if (!is_newline_char(cstr[count-1])) + bytes_written += s->EOL(); + } + return bytes_written; + } + return 0; +} + +void +SourceManager::File::FindLinesMatchingRegex (RegularExpression& regex, uint32_t start_line, uint32_t end_line, std::vector<uint32_t> &match_lines) +{ + TimeValue curr_mod_time (m_file_spec.GetModificationTime()); + if (m_mod_time != curr_mod_time) + { + m_mod_time = curr_mod_time; + m_data_sp = m_file_spec.ReadFileContents (); + m_offsets.clear(); + } + + 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.c_str())) + { + 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) + { + if (lhs.m_mod_time.IsValid()) + { + if (rhs.m_mod_time.IsValid()) + return lhs.m_mod_time == rhs.m_mod_time; + else + return false; + } + else if (rhs.m_mod_time.IsValid()) + return false; + else + return true; + } + else + return false; +} + +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() == NULL) + 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); + register const char *s; + for (s = start; s < end; ++s) + { + register char curr_ch = *s; + if (is_newline_char (curr_ch)) + { + if (s + 1 < end) + { + register 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() < 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" == NULL); + } + + } + else + { + // Calculate all line offsets up to "line" + assert("Not implemented yet" == NULL); + } + 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/source/Core/State.cpp b/source/Core/State.cpp new file mode 100644 index 0000000..7d9ccda --- /dev/null +++ b/source/Core/State.cpp @@ -0,0 +1,115 @@ +//===-- State.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/State.h" +#include <stdio.h> + +using namespace lldb; +using namespace lldb_private; + +const char * +lldb_private::StateAsCString (StateType state) +{ + switch (state) + { + case eStateInvalid: return "invalid"; + case eStateUnloaded: return "unloaded"; + case eStateConnected: return "connected"; + case eStateAttaching: return "attaching"; + case eStateLaunching: return "launching"; + case eStateStopped: return "stopped"; + case eStateRunning: return "running"; + case eStateStepping: return "stepping"; + case eStateCrashed: return "crashed"; + case eStateDetached: return "detached"; + case eStateExited: return "exited"; + case eStateSuspended: return "suspended"; + } + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "StateType = %i", state); + return unknown_state_string; +} + +const char * +lldb_private::GetPermissionsAsCString (uint32_t permissions) +{ + switch (permissions) + { + case 0: return "---"; + case ePermissionsWritable: return "-w-"; + case ePermissionsReadable: return "r--"; + case ePermissionsExecutable: return "--x"; + case ePermissionsReadable | + ePermissionsWritable: return "rw-"; + case ePermissionsReadable | + ePermissionsExecutable: return "r-x"; + case ePermissionsWritable | + ePermissionsExecutable: return "-wx"; + case ePermissionsReadable | + ePermissionsWritable | + ePermissionsExecutable: return "rwx"; + default: + break; + } + return "???"; +} + +bool +lldb_private::StateIsRunningState (StateType state) +{ + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + return true; + + case eStateConnected: + case eStateDetached: + case eStateInvalid: + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + break; + } + return false; +} + +bool +lldb_private::StateIsStoppedState (StateType state, bool must_exist) +{ + switch (state) + { + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + break; + + case eStateUnloaded: + case eStateExited: + return !must_exist; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + return true; + } + return false; +} diff --git a/source/Core/Stream.cpp b/source/Core/Stream.cpp new file mode 100644 index 0000000..49c15d6 --- /dev/null +++ b/source/Core/Stream.cpp @@ -0,0 +1,786 @@ +//===-- Stream.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <inttypes.h> + +using namespace lldb; +using namespace lldb_private; + +Stream::Stream (uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : + m_flags (flags), + m_addr_size (addr_size), + m_byte_order (byte_order), + m_indent_level(0) +{ +} + +Stream::Stream () : + m_flags (0), + m_addr_size (4), + m_byte_order (lldb::endian::InlHostByteOrder()), + m_indent_level(0) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +Stream::~Stream () +{ +} + +ByteOrder +Stream::SetByteOrder (ByteOrder byte_order) +{ + ByteOrder old_byte_order = m_byte_order; + m_byte_order = byte_order; + return old_byte_order; +} + +//------------------------------------------------------------------ +// Put an offset "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +void +Stream::Offset (uint32_t uval, const char *format) +{ + Printf (format, uval); +} + +//------------------------------------------------------------------ +// Put an SLEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +size_t +Stream::PutSLEB128 (int64_t sval) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + bool more = true; + while (more) + { + uint8_t byte = sval & 0x7fu; + sval >>= 7; + /* sign bit of byte is 2nd high order bit (0x40) */ + if ((sval == 0 && !(byte & 0x40)) || + (sval == -1 && (byte & 0x40)) ) + more = false; + else + // more bytes to come + byte |= 0x80u; + bytes_written += Write(&byte, 1); + } + } + else + { + bytes_written = Printf ("0x%" PRIi64, sval); + } + + return bytes_written; + +} + +//------------------------------------------------------------------ +// Put an ULEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +size_t +Stream::PutULEB128 (uint64_t uval) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + do + { + + uint8_t byte = uval & 0x7fu; + uval >>= 7; + if (uval != 0) + { + // more bytes to come + byte |= 0x80u; + } + bytes_written += Write(&byte, 1); + } while (uval != 0); + } + else + { + bytes_written = Printf ("0x%" PRIx64, uval); + } + return bytes_written; +} + +//------------------------------------------------------------------ +// Print a raw NULL terminated C string to the stream. +//------------------------------------------------------------------ +size_t +Stream::PutCString (const char *cstr) +{ + size_t cstr_len = strlen(cstr); + // when in binary mode, emit the NULL terminator + if (m_flags.Test(eBinary)) + ++cstr_len; + return Write (cstr, cstr_len); +} + +//------------------------------------------------------------------ +// Print a double quoted NULL terminated C string to the stream +// using the printf format in "format". +//------------------------------------------------------------------ +void +Stream::QuotedCString (const char *cstr, const char *format) +{ + Printf (format, cstr); +} + +//------------------------------------------------------------------ +// Put an address "addr" out to the stream with optional prefix +// and suffix strings. +//------------------------------------------------------------------ +void +Stream::Address (uint64_t addr, uint32_t addr_size, const char *prefix, const char *suffix) +{ + if (prefix == NULL) + prefix = ""; + if (suffix == NULL) + suffix = ""; +// int addr_width = m_addr_size << 1; +// Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_width, addr, suffix); + Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_size * 2, (uint64_t)addr, suffix); +} + +//------------------------------------------------------------------ +// Put an address range out to the stream with optional prefix +// and suffix strings. +//------------------------------------------------------------------ +void +Stream::AddressRange(uint64_t lo_addr, uint64_t hi_addr, uint32_t addr_size, const char *prefix, const char *suffix) +{ + if (prefix && prefix[0]) + PutCString (prefix); + Address (lo_addr, addr_size, "["); + Address (hi_addr, addr_size, "-", ")"); + if (suffix && suffix[0]) + PutCString (suffix); +} + + +size_t +Stream::PutChar (char ch) +{ + return Write (&ch, 1); +} + + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +Stream::Printf (const char *format, ...) +{ + va_list args; + va_start (args, format); + size_t result = PrintfVarArg(format, args); + va_end (args); + return result; +} + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +size_t +Stream::PrintfVarArg (const char *format, va_list args) +{ + char str[1024]; + va_list args_copy; + + va_copy (args_copy, args); + + size_t bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + // Include the NULL termination byte for binary output + if (m_flags.Test(eBinary)) + length += 1; + // The formatted string fit into our stack based buffer, so we can just + // append that to our packet + bytes_written = Write (str, length); + } + else + { + // Our stack buffer wasn't big enough to contain the entire formatted + // string, so lets let vasprintf create the string for us! + char *str_ptr = NULL; + length = ::vasprintf (&str_ptr, format, args_copy); + if (str_ptr) + { + // Include the NULL termination byte for binary output + if (m_flags.Test(eBinary)) + length += 1; + bytes_written = Write (str_ptr, length); + ::free (str_ptr); + } + } + va_end (args_copy); + return bytes_written; +} + +//------------------------------------------------------------------ +// Print and End of Line character to the stream +//------------------------------------------------------------------ +size_t +Stream::EOL() +{ + return PutChar ('\n'); +} + +//------------------------------------------------------------------ +// Indent the current line using the current indentation level and +// print an optional string following the idenatation spaces. +//------------------------------------------------------------------ +size_t +Stream::Indent(const char *s) +{ + return Printf ("%*.*s%s", m_indent_level, m_indent_level, "", s ? s : ""); +} + +//------------------------------------------------------------------ +// Stream a character "ch" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (char ch) +{ + PutChar (ch); + return *this; +} + +//------------------------------------------------------------------ +// Stream the NULL terminated C string out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (const char *s) +{ + Printf ("%s", s); + return *this; +} + +//------------------------------------------------------------------ +// Stream the pointer value out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (void *p) +{ + Printf ("0x%.*tx", (int)sizeof(void*) * 2, (ptrdiff_t)p); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint8_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint8_t uval) +{ + PutHex8(uval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint16_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint16_t uval) +{ + PutHex16(uval, m_byte_order); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint32_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint32_t uval) +{ + PutHex32(uval, m_byte_order); + return *this; +} + +//------------------------------------------------------------------ +// Stream a uint64_t "uval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (uint64_t uval) +{ + PutHex64(uval, m_byte_order); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int8_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int8_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int16_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int16_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int32_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int32_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int64_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int64_t sval) +{ + Printf ("%" PRIi64, sval); + return *this; +} + +//------------------------------------------------------------------ +// Get the current indentation level +//------------------------------------------------------------------ +int +Stream::GetIndentLevel() const +{ + return m_indent_level; +} + +//------------------------------------------------------------------ +// Set the current indentation level +//------------------------------------------------------------------ +void +Stream::SetIndentLevel(int indent_level) +{ + m_indent_level = indent_level; +} + +//------------------------------------------------------------------ +// Increment the current indentation level +//------------------------------------------------------------------ +void +Stream::IndentMore(int amount) +{ + m_indent_level += amount; +} + +//------------------------------------------------------------------ +// Decrement the current indentation level +//------------------------------------------------------------------ +void +Stream::IndentLess (int amount) +{ + if (m_indent_level >= amount) + m_indent_level -= amount; + else + m_indent_level = 0; +} + +//------------------------------------------------------------------ +// Get the address size in bytes +//------------------------------------------------------------------ +uint32_t +Stream::GetAddressByteSize() const +{ + return m_addr_size; +} + +//------------------------------------------------------------------ +// Set the address size in bytes +//------------------------------------------------------------------ +void +Stream::SetAddressByteSize(uint32_t addr_size) +{ + m_addr_size = addr_size; +} + +//------------------------------------------------------------------ +// Returns true if the verbose flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetVerbose() const +{ + return m_flags.Test(eVerbose); +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetDebug() const +{ + return m_flags.Test(eDebug); +} + +//------------------------------------------------------------------ +// The flags get accessor +//------------------------------------------------------------------ +Flags& +Stream::GetFlags() +{ + return m_flags; +} + +//------------------------------------------------------------------ +// The flags const get accessor +//------------------------------------------------------------------ +const Flags& +Stream::GetFlags() const +{ + return m_flags; +} + +//------------------------------------------------------------------ +// The byte order get accessor +//------------------------------------------------------------------ + +lldb::ByteOrder +Stream::GetByteOrder() const +{ + return m_byte_order; +} + +size_t +Stream::PrintfAsRawHex8 (const char *format, ...) +{ + va_list args; + va_list args_copy; + va_start (args, format); + va_copy (args, args_copy); // Copy this so we + + char str[1024]; + size_t bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + // The formatted string fit into our stack based buffer, so we can just + // append that to our packet + for (size_t i=0; i<length; ++i) + bytes_written += _PutHex8 (str[i], false); + } + else + { + // Our stack buffer wasn't big enough to contain the entire formatted + // string, so lets let vasprintf create the string for us! + char *str_ptr = NULL; + length = ::vasprintf (&str_ptr, format, args_copy); + if (str_ptr) + { + for (size_t i=0; i<length; ++i) + bytes_written += _PutHex8 (str_ptr[i], false); + ::free (str_ptr); + } + } + va_end (args); + va_end (args_copy); + + return bytes_written; +} + +size_t +Stream::PutNHex8 (size_t n, uint8_t uvalue) +{ + size_t bytes_written = 0; + for (size_t i=0; i<n; ++i) + bytes_written += _PutHex8 (uvalue, m_flags.Test(eAddPrefix)); + return bytes_written; +} + +size_t +Stream::_PutHex8 (uint8_t uvalue, bool add_prefix) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + bytes_written = Write (&uvalue, 1); + } + else + { + if (add_prefix) + PutCString("0x"); + + static char g_hex_to_ascii_hex_char[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + char nibble_chars[2]; + nibble_chars[0] = g_hex_to_ascii_hex_char[(uvalue >> 4) & 0xf]; + nibble_chars[1] = g_hex_to_ascii_hex_char[(uvalue >> 0) & 0xf]; + bytes_written = Write (nibble_chars, sizeof(nibble_chars)); + } + return bytes_written; +} + +size_t +Stream::PutHex8 (uint8_t uvalue) +{ + return _PutHex8 (uvalue, m_flags.Test(eAddPrefix)); +} + +size_t +Stream::PutHex16 (uint16_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutMaxHex64 +( + uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order +) +{ + switch (byte_size) + { + case 1: return PutHex8 ((uint8_t)uvalue); + case 2: return PutHex16 ((uint16_t)uvalue); + case 4: return PutHex32 ((uint32_t)uvalue); + case 8: return PutHex64 (uvalue); + } + return 0; +} + +size_t +Stream::PutPointer (void *ptr) +{ + return PutRawBytes (&ptr, sizeof(ptr), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); +} + +size_t +Stream::PutFloat(float f, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&f, sizeof(f), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutDouble(double d, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&d, sizeof(d), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutLongDouble(long double ld, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&ld, sizeof(ld), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutRawBytes (const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) +{ + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + bool binary_was_set = m_flags.Test (eBinary); + if (!binary_was_set) + m_flags.Set (eBinary); + if (src_byte_order == dst_byte_order) + { + for (size_t i = 0; i < src_len; ++i) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (size_t i = src_len-1; i < src_len; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (!binary_was_set) + m_flags.Clear (eBinary); + + return bytes_written; +} + +size_t +Stream::PutBytesAsRawHex8 (const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) +{ + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + if (src_byte_order == dst_byte_order) + { + for (size_t i = 0; i < src_len; ++i) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (size_t i = src_len-1; i < src_len; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return bytes_written; +} + +size_t +Stream::PutCStringAsRawHex8 (const char *s) +{ + size_t bytes_written = 0; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + do + { + bytes_written += _PutHex8 (*s, false); + ++s; + } while (*s); + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + +void +Stream::UnitTest(Stream *s) +{ + s->PutHex8(0x12); + + s->PutChar(' '); + s->PutHex16(0x3456, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderBig); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex32(0x789abcde, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderBig); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderBig); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderLittle); + + const char *hola = "Hello World!!!"; + s->PutChar(' '); + s->PutCString (hola); + + s->PutChar(' '); + s->Write (hola, 5); + + s->PutChar(' '); + s->PutCStringAsRawHex8 (hola); + + s->PutChar(' '); + s->PutCStringAsRawHex8 ("01234"); + + s->PutChar(' '); + s->Printf ("pid=%i", 12733); + + s->PutChar(' '); + s->PrintfAsRawHex8 ("pid=%i", 12733); + s->PutChar('\n'); +} + diff --git a/source/Core/StreamAsynchronousIO.cpp b/source/Core/StreamAsynchronousIO.cpp new file mode 100644 index 0000000..b9e5cdf --- /dev/null +++ b/source/Core/StreamAsynchronousIO.cpp @@ -0,0 +1,52 @@ +//===-- StreamBroadcast.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamAsynchronousIO.h" + +using namespace lldb; +using namespace lldb_private; + + +StreamAsynchronousIO::StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type) : + Stream (0, 4, eByteOrderBig), + m_broadcaster (broadcaster), + m_broadcast_event_type (broadcast_event_type), + m_accumulated_data () +{ +} + +StreamAsynchronousIO::~StreamAsynchronousIO () +{ +} + +void +StreamAsynchronousIO::Flush () +{ + if (m_accumulated_data.GetSize() > 0) + { + std::unique_ptr<EventDataBytes> data_bytes_ap (new EventDataBytes); + // Let's swap the bytes to avoid LARGE string copies. + data_bytes_ap->SwapBytes (m_accumulated_data.GetString()); + EventSP new_event_sp (new Event (m_broadcast_event_type, data_bytes_ap.release())); + m_broadcaster.BroadcastEvent (new_event_sp); + m_accumulated_data.Clear(); + } +} + +size_t +StreamAsynchronousIO::Write (const void *s, size_t length) +{ + m_accumulated_data.Write (s, length); + return length; +} diff --git a/source/Core/StreamCallback.cpp b/source/Core/StreamCallback.cpp new file mode 100644 index 0000000..d144b16 --- /dev/null +++ b/source/Core/StreamCallback.cpp @@ -0,0 +1,64 @@ +//===-- StreamCallback.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamCallback.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + + +StreamCallback::StreamCallback (lldb::LogOutputCallback callback, void *baton) : + Stream (0, 4, eByteOrderBig), + m_callback (callback), + m_baton (baton), + m_accumulated_data (), + m_collection_mutex () +{ +} + +StreamCallback::~StreamCallback () +{ +} + +StreamString & +StreamCallback::FindStreamForThread(lldb::tid_t cur_tid) +{ + Mutex::Locker locker(m_collection_mutex); + collection::iterator iter = m_accumulated_data.find (cur_tid); + if (iter == m_accumulated_data.end()) + { + std::pair<collection::iterator, bool> ret; + ret = m_accumulated_data.insert(std::pair<lldb::tid_t,StreamString>(cur_tid, StreamString())); + iter = ret.first; + } + return (*iter).second; +} + +void +StreamCallback::Flush () +{ + lldb::tid_t cur_tid = Host::GetCurrentThreadID(); + StreamString &out_stream = FindStreamForThread(cur_tid); + m_callback (out_stream.GetData(), m_baton); + out_stream.Clear(); +} + +size_t +StreamCallback::Write (const void *s, size_t length) +{ + lldb::tid_t cur_tid = Host::GetCurrentThreadID(); + FindStreamForThread(cur_tid).Write (s, length); + return length; +} diff --git a/source/Core/StreamFile.cpp b/source/Core/StreamFile.cpp new file mode 100644 index 0000000..9a4eb79 --- /dev/null +++ b/source/Core/StreamFile.cpp @@ -0,0 +1,72 @@ +//===-- StreamFile.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamFile.h" + +// C Includes +#include <stdio.h> +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" + + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StreamFile constructor +//---------------------------------------------------------------------- +StreamFile::StreamFile () : + Stream (), + m_file () +{ +} + +StreamFile::StreamFile (uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : + Stream (flags, addr_size, byte_order), + m_file () +{ +} + +StreamFile::StreamFile (int fd, bool transfer_ownership) : + Stream (), + m_file (fd, transfer_ownership) +{ +} + +StreamFile::StreamFile (FILE *fh, bool transfer_ownership) : + Stream (), + m_file (fh, transfer_ownership) +{ +} + +StreamFile::StreamFile (const char *path) : + Stream (), + m_file (path, File::eOpenOptionWrite | File::eOpenOptionCanCreate, File::ePermissionsDefault) +{ +} + + +StreamFile::~StreamFile() +{ +} + +void +StreamFile::Flush () +{ + m_file.Flush(); +} + +size_t +StreamFile::Write (const void *s, size_t length) +{ + m_file.Write (s, length); + return length; +} diff --git a/source/Core/StreamString.cpp b/source/Core/StreamString.cpp new file mode 100644 index 0000000..8d7d039 --- /dev/null +++ b/source/Core/StreamString.cpp @@ -0,0 +1,100 @@ +//===-- StreamString.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamString.h" +#include <stdio.h> + +using namespace lldb; +using namespace lldb_private; + +StreamString::StreamString () : + Stream (0, 4, eByteOrderBig) +{ +} + +StreamString::StreamString(uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : + Stream (flags, addr_size, byte_order), + m_packet () +{ +} + +StreamString::~StreamString() +{ +} + +void +StreamString::Flush () +{ + // Nothing to do when flushing a buffer based stream... +} + +size_t +StreamString::Write (const void *s, size_t length) +{ + m_packet.append ((char *)s, length); + return length; +} + +void +StreamString::Clear() +{ + m_packet.clear(); +} + +bool +StreamString::Empty() const +{ + return GetSize() == 0; +} + +const char * +StreamString::GetData () const +{ + return m_packet.c_str(); +} + +size_t +StreamString::GetSize () const +{ + return m_packet.size(); +} + +std::string & +StreamString::GetString() +{ + return m_packet; +} + +const std::string & +StreamString::GetString() const +{ + return m_packet; +} + +void +StreamString::FillLastLineToColumn (uint32_t column, char fill_char) +{ + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) + { + last_line_begin_pos = 0; + } + else + { + ++last_line_begin_pos; + } + + const size_t line_columns = length - last_line_begin_pos; + if (column > line_columns) + { + m_packet.append(column - line_columns, fill_char); + } +} + diff --git a/source/Core/StringList.cpp b/source/Core/StringList.cpp new file mode 100644 index 0000000..9949751 --- /dev/null +++ b/source/Core/StringList.cpp @@ -0,0 +1,290 @@ +//===-- StringList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StringList.h" + +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" + +#include <string> + +using namespace lldb_private; + +StringList::StringList () : + m_strings () +{ +} + +StringList::StringList (const char *str) : + m_strings () +{ + if (str) + m_strings.push_back (str); +} + +StringList::StringList (const char **strv, int strc) : + m_strings () +{ + for (int i = 0; i < strc; ++i) + { + if (strv[i]) + m_strings.push_back (strv[i]); + } +} + +StringList::~StringList () +{ +} + +void +StringList::AppendString (const char *str) +{ + if (str) + m_strings.push_back (str); +} + +void +StringList::AppendString (const std::string &s) +{ + m_strings.push_back (s); +} + +void +StringList::AppendString (const char *str, size_t str_len) +{ + if (str) + m_strings.push_back (std::string (str, str_len)); +} + +void +StringList::AppendList (const char **strv, int strc) +{ + for (int i = 0; i < strc; ++i) + { + if (strv[i]) + m_strings.push_back (strv[i]); + } +} + +void +StringList::AppendList (StringList strings) +{ + size_t len = strings.GetSize(); + + for (size_t i = 0; i < len; ++i) + m_strings.push_back (strings.GetStringAtIndex(i)); +} + +bool +StringList::ReadFileLines (FileSpec &input_file) +{ + return input_file.ReadFileLines (m_strings); +} + +size_t +StringList::GetSize () const +{ + return m_strings.size(); +} + +const char * +StringList::GetStringAtIndex (size_t idx) const +{ + if (idx < m_strings.size()) + return m_strings[idx].c_str(); + return NULL; +} + +void +StringList::Join (const char *separator, Stream &strm) +{ + size_t size = GetSize(); + + if (size == 0) + return; + + for (uint32_t i = 0; i < size; ++i) + { + if (i > 0) + strm.PutCString(separator); + strm.PutCString(GetStringAtIndex(i)); + } +} + +void +StringList::Clear () +{ + m_strings.clear(); +} + +void +StringList::LongestCommonPrefix (std::string &common_prefix) +{ + //arg_sstr_collection::iterator pos, end = m_args.end(); + size_t pos = 0; + size_t end = m_strings.size(); + + if (pos == end) + common_prefix.clear(); + else + common_prefix = m_strings[pos]; + + for (++pos; pos != end; ++pos) + { + size_t new_size = strlen (m_strings[pos].c_str()); + + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); + + // Then trim it at the first disparity: + + for (size_t i = 0; i < common_prefix.size(); i++) + { + if (m_strings[pos][i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } + } + + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } +} + +void +StringList::InsertStringAtIndex (size_t idx, const char *str) +{ + if (str) + { + if (idx < m_strings.size()) + m_strings.insert (m_strings.begin() + idx, str); + else + m_strings.push_back (str); + } +} + +void +StringList::DeleteStringAtIndex (size_t idx) +{ + if (idx < m_strings.size()) + m_strings.erase (m_strings.begin() + idx); +} + +size_t +StringList::SplitIntoLines (const char *lines, size_t len) +{ + const size_t orig_size = m_strings.size(); + + if (len == 0) + return 0; + + const char *k_newline_chars = "\r\n"; + const char *p = lines; + const char *end = lines + len; + while (p < end) + { + size_t count = strcspn (p, k_newline_chars); + if (count == 0) + { + if (p[count] == '\r' || p[count] == '\n') + m_strings.push_back(std::string()); + else + break; + } + else + { + if (p + count > end) + count = end - p; + m_strings.push_back(std::string(p, count)); + } + if (p[count] == '\r' && p[count+1] == '\n') + count++; // Skip an extra newline char for the DOS newline + count++; // Skip the newline character + p += count; + } + return m_strings.size() - orig_size; +} + +void +StringList::RemoveBlankLines () +{ + if (GetSize() == 0) + return; + + size_t idx = 0; + while (idx < m_strings.size()) + { + if (m_strings[idx].empty()) + DeleteStringAtIndex(idx); + else + idx++; + } +} + +std::string +StringList::CopyList(const char* item_preamble, + const char* items_sep) +{ + StreamString strm; + for (size_t i = 0; i < GetSize(); i++) + { + if (i && items_sep && items_sep[0]) + strm << items_sep; + if (item_preamble) + strm << item_preamble; + strm << GetStringAtIndex(i); + } + return std::string(strm.GetData()); +} + +StringList& +StringList::operator << (const char* str) +{ + AppendString(str); + return *this; +} + +StringList& +StringList::operator << (StringList strings) +{ + AppendList(strings); + return *this; +} + +size_t +StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) const +{ + matches.Clear(); + exact_idx = SIZE_MAX; + if (s && s[0]) + { + const size_t s_len = strlen (s); + const size_t num_strings = m_strings.size(); + + for (size_t i=0; i<num_strings; ++i) + { + if (m_strings[i].find(s) == 0) + { + if (exact_idx == SIZE_MAX && m_strings[i].size() == s_len) + exact_idx = matches.GetSize(); + matches.AppendString (m_strings[i]); + } + } + } + else + { + // No string, so it matches everything + matches = *this; + } + return matches.GetSize(); +} + diff --git a/source/Core/Timer.cpp b/source/Core/Timer.cpp new file mode 100644 index 0000000..b1416bd --- /dev/null +++ b/source/Core/Timer.cpp @@ -0,0 +1,250 @@ +//===-- Timer.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/Timer.h" + +#include <map> +#include <vector> +#include <algorithm> + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +#include <stdio.h> + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 +static bool g_quiet = true; +uint32_t Timer::g_depth = 0; +uint32_t Timer::g_display_depth = 0; +FILE * Timer::g_file = NULL; +typedef std::vector<Timer *> TimerStack; +typedef std::map<const char *, uint64_t> TimerCategoryMap; +static pthread_key_t g_key; + +static Mutex & +GetCategoryMutex() +{ + static Mutex g_category_mutex(Mutex::eMutexTypeNormal); + return g_category_mutex; +} + +static TimerCategoryMap & +GetCategoryMap() +{ + static TimerCategoryMap g_category_map; + return g_category_map; +} + + +static TimerStack * +GetTimerStackForCurrentThread () +{ + void *timer_stack = ::pthread_getspecific (g_key); + if (timer_stack == NULL) + { + ::pthread_setspecific (g_key, new TimerStack); + timer_stack = ::pthread_getspecific (g_key); + } + return (TimerStack *)timer_stack; +} + +void +ThreadSpecificCleanup (void *p) +{ + delete (TimerStack *)p; +} + +void +Timer::SetQuiet (bool value) +{ + g_quiet = value; +} + +void +Timer::Initialize () +{ + Timer::g_file = stdout; + ::pthread_key_create (&g_key, ThreadSpecificCleanup); + +} + +Timer::Timer (const char *category, const char *format, ...) : + m_category (category), + m_total_start (), + m_timer_start (), + m_total_ticks (0), + m_timer_ticks (0) +{ + if (g_depth++ < g_display_depth) + { + if (g_quiet == false) + { + // Indent + ::fprintf (g_file, "%*s", g_depth * TIMER_INDENT_AMOUNT, ""); + // Print formatted string + va_list args; + va_start (args, format); + ::vfprintf (g_file, format, args); + va_end (args); + + // Newline + ::fprintf (g_file, "\n"); + } + TimeValue start_time(TimeValue::Now()); + m_total_start = start_time; + m_timer_start = start_time; + TimerStack *stack = GetTimerStackForCurrentThread (); + if (stack) + { + if (stack->empty() == false) + stack->back()->ChildStarted (start_time); + stack->push_back(this); + } + } +} + + +Timer::~Timer() +{ + if (m_total_start.IsValid()) + { + TimeValue stop_time = TimeValue::Now(); + if (m_total_start.IsValid()) + { + m_total_ticks += (stop_time - m_total_start); + m_total_start.Clear(); + } + if (m_timer_start.IsValid()) + { + m_timer_ticks += (stop_time - m_timer_start); + m_timer_start.Clear(); + } + + TimerStack *stack = GetTimerStackForCurrentThread (); + if (stack) + { + assert (stack->back() == this); + stack->pop_back(); + if (stack->empty() == false) + stack->back()->ChildStopped(stop_time); + } + + const uint64_t total_nsec_uint = GetTotalElapsedNanoSeconds(); + const uint64_t timer_nsec_uint = GetTimerElapsedNanoSeconds(); + const double total_nsec = total_nsec_uint; + const double timer_nsec = timer_nsec_uint; + + if (g_quiet == false) + { + + ::fprintf (g_file, + "%*s%.9f sec (%.9f sec)\n", + (g_depth - 1) *TIMER_INDENT_AMOUNT, "", + total_nsec / 1000000000.0, + timer_nsec / 1000000000.0); + } + + // Keep total results for each category so we can dump results. + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + category_map[m_category] += timer_nsec_uint; + } + if (g_depth > 0) + --g_depth; +} + +uint64_t +Timer::GetTotalElapsedNanoSeconds() +{ + uint64_t total_ticks = m_total_ticks; + + // If we are currently running, we need to add the current + // elapsed time of the running timer... + if (m_total_start.IsValid()) + total_ticks += (TimeValue::Now() - m_total_start); + + return total_ticks; +} + +uint64_t +Timer::GetTimerElapsedNanoSeconds() +{ + uint64_t timer_ticks = m_timer_ticks; + + // If we are currently running, we need to add the current + // elapsed time of the running timer... + if (m_timer_start.IsValid()) + timer_ticks += (TimeValue::Now() - m_timer_start); + + return timer_ticks; +} + +void +Timer::ChildStarted (const TimeValue& start_time) +{ + if (m_timer_start.IsValid()) + { + m_timer_ticks += (start_time - m_timer_start); + m_timer_start.Clear(); + } +} + +void +Timer::ChildStopped (const TimeValue& stop_time) +{ + if (!m_timer_start.IsValid()) + m_timer_start = stop_time; +} + +void +Timer::SetDisplayDepth (uint32_t depth) +{ + g_display_depth = depth; +} + + +/* binary function predicate: + * - returns whether a person is less than another person + */ +static bool +CategoryMapIteratorSortCriterion (const TimerCategoryMap::const_iterator& lhs, const TimerCategoryMap::const_iterator& rhs) +{ + return lhs->second > rhs->second; +} + + +void +Timer::ResetCategoryTimes () +{ + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + category_map.clear(); +} + +void +Timer::DumpCategoryTimes (Stream *s) +{ + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + std::vector<TimerCategoryMap::const_iterator> sorted_iterators; + TimerCategoryMap::const_iterator pos, end = category_map.end(); + for (pos = category_map.begin(); pos != end; ++pos) + { + sorted_iterators.push_back (pos); + } + std::sort (sorted_iterators.begin(), sorted_iterators.end(), CategoryMapIteratorSortCriterion); + + const size_t count = sorted_iterators.size(); + for (size_t i=0; i<count; ++i) + { + const double timer_nsec = sorted_iterators[i]->second; + s->Printf("%.9f sec for %s\n", timer_nsec / 1000000000.0, sorted_iterators[i]->first); + } +} diff --git a/source/Core/UUID.cpp b/source/Core/UUID.cpp new file mode 100644 index 0000000..c1b3eb1 --- /dev/null +++ b/source/Core/UUID.cpp @@ -0,0 +1,279 @@ +//===-- UUID.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/UUID.h" +// C Includes +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +UUID::UUID() : m_num_uuid_bytes(16) +{ + ::memset (m_uuid, 0, sizeof(m_uuid)); +} + +UUID::UUID(const UUID& rhs) +{ + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); +} + +UUID::UUID (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + SetBytes (uuid_bytes, num_uuid_bytes); +} + +const UUID& +UUID::operator=(const UUID& rhs) +{ + if (this != &rhs) + { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); + } + return *this; +} + +UUID::~UUID() +{ +} + +void +UUID::Clear() +{ + m_num_uuid_bytes = 16; + ::memset (m_uuid, 0, sizeof(m_uuid)); +} + +const void * +UUID::GetBytes() const +{ + return m_uuid; +} + +std::string +UUID::GetAsString (const char *separator) const +{ + std::string result; + char buf[256]; + if (!separator) + separator = "-"; + const uint8_t *u = (const uint8_t *)GetBytes(); + if (sizeof (buf) > (size_t)snprintf (buf, + sizeof (buf), + "%2.2X%2.2X%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", + u[0],u[1],u[2],u[3],separator, + u[4],u[5],separator, + u[6],u[7],separator, + u[8],u[9],separator, + u[10],u[11],u[12],u[13],u[14],u[15])) + { + result.append (buf); + if (m_num_uuid_bytes == 20) + { + if (sizeof (buf) > (size_t)snprintf (buf, sizeof (buf), "%s%2.2X%2.2X%2.2X%2.2X", separator,u[16],u[17],u[18],u[19])) + result.append (buf); + } + } + return result; +} + +void +UUID::Dump (Stream *s) const +{ + const uint8_t *u = (const uint8_t *)GetBytes(); + s->Printf ("%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", + u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8],u[9],u[10],u[11],u[12],u[13],u[14],u[15]); + if (m_num_uuid_bytes == 20) + { + s->Printf ("-%2.2X%2.2X%2.2X%2.2X", u[16],u[17],u[18],u[19]); + } +} + +bool +UUID::SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + if (uuid_bytes) + { + switch (num_uuid_bytes) + { + case 20: + m_num_uuid_bytes = 20; + break; + case 16: + m_num_uuid_bytes = 16; + m_uuid[16] = m_uuid[17] = m_uuid[18] = m_uuid[19] = 0; + break; + default: + // Unsupported UUID byte size + m_num_uuid_bytes = 0; + break; + } + + if (m_num_uuid_bytes > 0) + { + ::memcpy (m_uuid, uuid_bytes, m_num_uuid_bytes); + return true; + } + } + ::memset (m_uuid, 0, sizeof(m_uuid)); + return false; +} + +size_t +UUID::GetByteSize() +{ + return m_num_uuid_bytes; +} + +bool +UUID::IsValid () const +{ + return m_uuid[0] || + m_uuid[1] || + m_uuid[2] || + m_uuid[3] || + m_uuid[4] || + m_uuid[5] || + m_uuid[6] || + m_uuid[7] || + m_uuid[8] || + m_uuid[9] || + m_uuid[10] || + m_uuid[11] || + m_uuid[12] || + m_uuid[13] || + m_uuid[14] || + m_uuid[15] || + m_uuid[16] || + m_uuid[17] || + m_uuid[18] || + m_uuid[19]; +} + +static inline int +xdigit_to_int (char ch) +{ + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + return ch - '0'; +} + +size_t +UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes) +{ + size_t uuid_byte_idx = 0; + if (p) + { + while (*p) + { + if (isxdigit(p[0]) && isxdigit(p[1])) + { + int hi_nibble = xdigit_to_int(p[0]); + int lo_nibble = xdigit_to_int(p[1]); + // Translate the two hex nibble characters into a byte + uuid_bytes[uuid_byte_idx] = (hi_nibble << 4) + lo_nibble; + + // Skip both hex digits + p += 2; + + // Increment the byte that we are decoding within the UUID value + // and break out if we are done + if (++uuid_byte_idx == num_uuid_bytes) + break; + } + else if (*p == '-') + { + // Skip dashes + p++; + } + else + { + // UUID values can only consist of hex characters and '-' chars + break; + } + } + } + if (end) + *end = p; + // Clear trailing bytes to 0. + for (uint32_t i = uuid_byte_idx; i < sizeof(ValueType); i++) + uuid_bytes[i] = 0; + return uuid_byte_idx; +} +size_t +UUID::SetFromCString (const char *cstr, uint32_t num_uuid_bytes) +{ + if (cstr == NULL) + return 0; + + const char *p = cstr; + + // Skip leading whitespace characters + while (isspace(*p)) + ++p; + + const size_t uuid_byte_idx = UUID::DecodeUUIDBytesFromCString (p, m_uuid, &p, num_uuid_bytes); + + // If we successfully decoded a UUID, return the amount of characters that + // were consumed + if (uuid_byte_idx == num_uuid_bytes) + return p - cstr; + + // Else return zero to indicate we were not able to parse a UUID value + return 0; +} + +} + +bool +lldb_private::operator == (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) == 0; +} + +bool +lldb_private::operator != (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) != 0; +} + +bool +lldb_private::operator < (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) < 0; +} + +bool +lldb_private::operator <= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) <= 0; +} + +bool +lldb_private::operator > (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) > 0; +} + +bool +lldb_private::operator >= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) >= 0; +} diff --git a/source/Core/UserID.cpp b/source/Core/UserID.cpp new file mode 100644 index 0000000..f3d6e5b --- /dev/null +++ b/source/Core/UserID.cpp @@ -0,0 +1,23 @@ +//===-- UserID.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/UserID.h" +#include "lldb/Core/Stream.h" + +#include <inttypes.h> + +using namespace lldb; +using namespace lldb_private; + +Stream& +lldb_private::operator << (Stream& strm, const UserID& uid) +{ + strm.Printf("{0x%8.8" PRIx64 "}", uid.GetID()); + return strm; +} diff --git a/source/Core/UserSettingsController.cpp b/source/Core/UserSettingsController.cpp new file mode 100644 index 0000000..63a5dd9 --- /dev/null +++ b/source/Core/UserSettingsController.cpp @@ -0,0 +1,111 @@ +//====-- UserSettingsController.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include <string.h> +#include <algorithm> + +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueString.h" + +using namespace lldb; +using namespace lldb_private; + + +lldb::OptionValueSP +Properties::GetPropertyValue (const ExecutionContext *exe_ctx, + const char *path, + bool will_modify, + Error &error) const +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->GetSubValue(exe_ctx, path, will_modify, error); + return lldb::OptionValueSP(); +} + +Error +Properties::SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *path, + const char *value) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->SetSubValue(exe_ctx, op, path, value); + Error 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); +} + + + +Error +Properties::DumpPropertyValue (const ExecutionContext *exe_ctx, Stream &strm, const char *property_path, uint32_t dump_mask) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + { + return properties_sp->DumpPropertyValue (exe_ctx, + strm, + property_path, + dump_mask); + } + Error error; + error.SetErrorString("empty property list"); + return error; +} + +size_t +Properties::Apropos (const char *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, + const ConstString &name) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->GetSubProperty (exe_ctx, name); + return lldb::OptionValuePropertiesSP(); +} + diff --git a/source/Core/VMRange.cpp b/source/Core/VMRange.cpp new file mode 100644 index 0000000..902489e --- /dev/null +++ b/source/Core/VMRange.cpp @@ -0,0 +1,112 @@ +//===-- VMRange.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/VMRange.h" +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +bool +VMRange::ContainsValue(const VMRange::collection& coll, lldb::addr_t value) +{ + ValueInRangeUnaryPredicate in_range_predicate(value); + VMRange::const_iterator pos; + VMRange::const_iterator end = coll.end(); + pos = std::find_if( coll.begin(), end, in_range_predicate ); + if (pos != end) + return true; + return false; +} + +bool +VMRange::ContainsRange(const VMRange::collection& coll, const VMRange& range) +{ + RangeInRangeUnaryPredicate in_range_predicate(range); + VMRange::const_iterator pos; + VMRange::const_iterator end = coll.end(); + pos = std::find_if( coll.begin(), end, in_range_predicate ); + if (pos != end) + return true; + return false; +} + +size_t +VMRange::FindRangeIndexThatContainsValue (const VMRange::collection& coll, lldb::addr_t value) +{ + ValueInRangeUnaryPredicate in_range_predicate(value); + VMRange::const_iterator begin = coll.begin(); + VMRange::const_iterator end = coll.end(); + VMRange::const_iterator pos = std::find_if (begin, end, in_range_predicate); + if (pos != end) + return std::distance (begin, pos); + return UINT32_MAX; +} + +void +VMRange::Dump(Stream *s, lldb::addr_t offset, uint32_t addr_width) const +{ + s->AddressRange(offset + GetBaseAddress(), offset + GetEndAddress(), addr_width); +} + +bool +lldb_private::operator== (const VMRange& lhs, const VMRange& rhs) +{ + return lhs.GetBaseAddress() == rhs.GetBaseAddress() && lhs.GetEndAddress() == rhs.GetEndAddress(); +} + +bool +lldb_private::operator!= (const VMRange& lhs, const VMRange& rhs) +{ + return lhs.GetBaseAddress() != rhs.GetBaseAddress() || lhs.GetEndAddress() != rhs.GetEndAddress(); +} + +bool +lldb_private::operator< (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() < rhs.GetEndAddress(); +} + +bool +lldb_private::operator<= (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() <= rhs.GetEndAddress(); +} + +bool +lldb_private::operator> (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() > rhs.GetEndAddress(); +} + +bool +lldb_private::operator>= (const VMRange& lhs, const VMRange& rhs) +{ + if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() >= rhs.GetEndAddress(); +} + diff --git a/source/Core/Value.cpp b/source/Core/Value.cpp new file mode 100644 index 0000000..3fe75d3 --- /dev/null +++ b/source/Core/Value.cpp @@ -0,0 +1,761 @@ +//===-- Value.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Value.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.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/Target.h" + +using namespace lldb; +using namespace lldb_private; + +Value::Value() : + m_value (), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeScalar), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ +} + +Value::Value(const Scalar& scalar) : + m_value (scalar), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeScalar), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ +} + + +Value::Value(const uint8_t *bytes, int len) : + m_value (), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeHostAddress), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ + m_data_buffer.CopyData(bytes, len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +Value::Value(const Value &v) : + m_value (v.m_value), + m_vector (v.m_vector), + m_clang_type (v.m_clang_type), + m_context (v.m_context), + m_value_type (v.m_value_type), + m_context_type (v.m_context_type), + m_data_buffer () +{ + if ((uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (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_clang_type = rhs.m_clang_type; + m_context = rhs.m_context; + m_value_type = rhs.m_value_type; + m_context_type = rhs.m_context_type; + if ((uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (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::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 NULL; +} + +Type * +Value::GetType() +{ + if (m_context_type == eContextTypeLLDBType) + return static_cast<Type *> (m_context); + return NULL; +} + +void +Value::ResizeData(size_t len) +{ + m_value_type = eValueTypeHostAddress; + m_data_buffer.SetByteSize(len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +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 (Error *error_ptr) +{ + uint64_t byte_size = 0; + + switch (m_context_type) + { + case eContextTypeRegisterInfo: // RegisterInfo * + if (GetRegisterInfo()) + byte_size = GetRegisterInfo()->byte_size; + break; + + case eContextTypeInvalid: + case eContextTypeLLDBType: // Type * + case eContextTypeVariable: // Variable * + { + const ClangASTType &ast_type = GetClangType(); + if (ast_type.IsValid()) + byte_size = ast_type.GetByteSize(); + } + break; + } + + if (error_ptr) + { + if (byte_size == 0) + { + if (error_ptr->Success()) + error_ptr->SetErrorString("Unable to determine byte size."); + } + else + { + error_ptr->Clear(); + } + } + return byte_size; +} + +const ClangASTType & +Value::GetClangType () +{ + if (!m_clang_type.IsValid()) + { + switch (m_context_type) + { + case eContextTypeInvalid: + break; + + case eContextTypeRegisterInfo: + break; // TODO: Eventually convert into a clang type? + + case eContextTypeLLDBType: + { + Type *lldb_type = GetType(); + if (lldb_type) + m_clang_type = lldb_type->GetClangForwardType(); + } + break; + + case eContextTypeVariable: + { + Variable *variable = GetVariable(); + if (variable) + { + Type *variable_type = variable->GetType(); + if (variable_type) + m_clang_type = variable_type->GetClangForwardType(); + } + } + break; + } + } + + return m_clang_type; +} + +void +Value::SetClangType (const ClangASTType &clang_type) +{ + m_clang_type = clang_type; +} + +lldb::Format +Value::GetValueDefaultFormat () +{ + switch (m_context_type) + { + case eContextTypeRegisterInfo: + if (GetRegisterInfo()) + return GetRegisterInfo()->format; + break; + + case eContextTypeInvalid: + case eContextTypeLLDBType: + case eContextTypeVariable: + { + const ClangASTType &ast_type = GetClangType(); + 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; + +} + +Error +Value::GetValueAsData (ExecutionContext *exe_ctx, + DataExtractor &data, + uint32_t data_offset, + Module *module) +{ + data.Clear(); + + Error error; + lldb::addr_t address = LLDB_INVALID_ADDRESS; + AddressType address_type = eAddressTypeFile; + Address file_so_addr; + const ClangASTType &ast_type = GetClangType(); + 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 (lldb::endian::InlHostByteOrder()); + if (ast_type.IsValid()) + data.SetAddressByteSize (ast_type.GetPointerByteSize()); + else + data.SetAddressByteSize(sizeof(void *)); + if (m_value.GetData (data)) + return error; // Success; + error.SetErrorStringWithFormat("extracting data from value failed"); + break; + + case eValueTypeLoadAddress: + if (exe_ctx == NULL) + { + error.SetErrorString ("can't read load address (no execution context)"); + } + else + { + Process *process = exe_ctx->GetProcessPtr(); + if (process == NULL || !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 +// { +// ModuleSP exe_module_sp (target->GetExecutableModule()); +// if (exe_module_sp) +// { +// address = m_value.ULongLong(LLDB_INVALID_ADDRESS); +// if (address != LLDB_INVALID_ADDRESS) +// { +// if (exe_module_sp->ResolveFileAddress(address, file_so_addr)) +// { +// data.SetByteOrder(target->GetArchitecture().GetByteOrder()); +// data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); +// address_type = eAddressTypeFile; +// } +// 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 == NULL) + { + error.SetErrorString ("can't read file address (no execution context)"); + } + else if (exe_ctx->GetTargetPtr() == NULL) + { + 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 == NULL) + { + // 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(lldb::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); + + // Bail if we encountered any errors getting the byte size + if (error.Fail()) + return error; + + // Make sure we have enough room within "data", and if we don't make + // something large enough that does + if (!data.ValidOffsetForDataOfSize (data_offset, byte_size)) + { + DataBufferSP data_sp(new DataBufferHeap (data_offset + byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = const_cast<uint8_t*>(data.PeekData (data_offset, byte_size)); + if (dst != NULL) + { + if (address_type == eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy (dst, (uint8_t*)NULL + 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 ClangASTType &clang_type = GetClangType(); + if (clang_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); + Error error (GetValueAsData (exe_ctx, data, 0, NULL)); + if (error.Success()) + { + Scalar scalar; + if (clang_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 NULL; +} + +void +Value::Clear() +{ + m_value.Clear(); + m_vector.Clear(); + m_clang_type.Clear(); + m_value_type = eValueTypeScalar; + m_context = NULL; + 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 "???"; +} + +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 NULL; +} + +void +ValueList::Clear () +{ + m_values.clear(); +} + diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp new file mode 100644 index 0000000..a30cc13 --- /dev/null +++ b/source/Core/ValueObject.cpp @@ -0,0 +1,4199 @@ +//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObject.h" + +// C Includes +#include <stdlib.h> + +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/raw_ostream.h" +#include "clang/AST/Type.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectCast.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +#include "lldb/Host/Endian.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_utility; + +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 (NULL), + 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_manager(parent.GetManager()), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + 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_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), + 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_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 (NULL), + m_root (NULL), + 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_manager(), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + 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_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), + 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_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 were asked to update your formatters, but did not get a chance to do it + // clear your own values (this serves the purpose of faking a stop-id for frozen + // objects (which are regarded as constant, but could have changes behind their backs + // because of the frozen-pointer depth limit) + // TODO: decouple summary from value and then remove this code and only force-clear the summary + if (update_format && !did_change_formats) + ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + return m_error.Success(); + } + + bool first_update = m_update_point.IsFirstEvaluation(); + + if (m_update_point.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 success = UpdateValue (); + + SetValueIsValid (success); + + if (first_update) + SetValueDidChange (false); + else if (!m_value_did_change && success == false) + { + // 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 + { + m_error.SetErrorString("out of scope"); + } + } + return m_error.Success(); +} + +bool +ValueObject::UpdateFormatsIfNeeded() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + if (log) + log->Printf("[%s %p] checking for FormatManager revisions. ValueObject rev: %d - Global rev: %d", + GetName().GetCString(), + this, + m_last_format_mgr_revision, + DataVisualization::GetCurrentRevision()); + + bool any_change = false; + + if ( (m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) + { + SetValueFormat(DataVisualization::ValueFormats::GetFormat (*this, eNoDynamicValues)); + SetSummaryFormat(DataVisualization::GetSummaryFormat (*this, GetDynamicValueType())); +#ifndef LLDB_DISABLE_PYTHON + SetSyntheticChildren(DataVisualization::GetSyntheticChildren (*this, GetDynamicValueType())); +#endif + + m_last_format_mgr_revision = DataVisualization::GetCurrentRevision(); + + any_change = true; + } + + 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_did_calculate_complete_objc_class_type = false; + m_last_format_mgr_revision = 0; + m_override_type = ClangASTType(); + SetValueFormat(lldb::TypeFormatImplSP()); + SetSummaryFormat(lldb::TypeSummaryImplSP()); + SetSyntheticChildren(lldb::SyntheticChildrenSP()); +} + +ClangASTType +ValueObject::MaybeCalculateCompleteType () +{ + ClangASTType clang_type(GetClangTypeImpl()); + + if (m_did_calculate_complete_objc_class_type) + { + if (m_override_type.IsValid()) + return m_override_type; + else + return clang_type; + } + + ClangASTType class_type; + bool is_pointer_type = false; + + if (clang_type.IsObjCObjectPointerType(&class_type)) + { + is_pointer_type = true; + } + else if (clang_type.IsObjCObjectOrInterfaceType()) + { + class_type = clang_type; + } + else + { + return clang_type; + } + + m_did_calculate_complete_objc_class_type = true; + + if (class_type) + { + ConstString class_name (class_type.GetConstTypeName()); + + if (class_name) + { + ProcessSP process_sp(GetUpdatePoint().GetExecutionContextRef().GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime(process_sp->GetObjCLanguageRuntime()); + + if (objc_language_runtime) + { + TypeSP complete_objc_class_type_sp = objc_language_runtime->LookupInCompleteClassCache(class_name); + + if (complete_objc_class_type_sp) + { + ClangASTType complete_class(complete_objc_class_type_sp->GetClangFullType()); + + if (complete_class.GetCompleteType()) + { + if (is_pointer_type) + { + m_override_type = complete_class.GetPointerType(); + } + else + { + m_override_type = complete_class; + } + + if (m_override_type.IsValid()) + return m_override_type; + } + } + } + } + } + } + return clang_type; +} + +ClangASTType +ValueObject::GetClangType () +{ + return MaybeCalculateCompleteType(); +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + UpdateValueIfNeeded(false); + return m_data; +} + +const Error & +ValueObject::GetError() +{ + UpdateValueIfNeeded(false); + return m_error; +} + +const 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.swap(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::GetValueIsValid () const +{ + return m_value_is_valid; +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + m_value_is_valid = b; +} + +bool +ValueObject::GetValueDidChange () +{ + GetValueAsCString (); + 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 != NULL) + return child->GetSP(); + } + return child_sp; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list<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; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list< 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::GetChildAtIndexPath (const std::vector<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 (const std::vector< 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; +} + +size_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return GetClangType().GetIndexOfChildWithName (name.GetCString(), omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const 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 = GetClangType().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 () +{ + UpdateValueIfNeeded(); + 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 & (ClangASTType::eTypeHasChildren | + ClangASTType::eTypeIsPointer | + ClangASTType::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 (const ConstString &name) +{ + m_name = name; +} + +ValueObject * +ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObject *valobj = NULL; + + 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; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 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); + if (child_clang_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_clang_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); + //if (valobj) + // valobj->SetAddressTypeOfChildren(eAddressTypeInvalid); + } + + return valobj; +} + +bool +ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination) +{ + 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; + + // 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)) + { + if (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); + } + else + { + ClangASTType clang_type = GetClangType(); + + // Do some default printout for function pointers + if (clang_type) + { + if (clang_type.IsFunctionPointerType ()) + { + StreamString sstr; + AddressType func_ptr_address_type = eAddressTypeInvalid; + addr_t func_ptr_address = GetPointerValue (&func_ptr_address_type); + if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) + { + switch (func_ptr_address_type) + { + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + + case eAddressTypeLoad: + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + Address so_addr; + Target *target = exe_ctx.GetTargetPtr(); + if (target && target->GetSectionLoadList().IsEmpty() == false) + { + if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr)) + { + so_addr.Dump (&sstr, + exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + } + } + } + break; + + case eAddressTypeHost: + break; + } + } + if (sstr.GetSize() > 0) + { + destination.assign (1, '('); + destination.append (sstr.GetData(), sstr.GetSize()); + destination.append (1, ')'); + } + } + } + } + } + m_is_getting_summary = false; + return !destination.empty(); +} + +const char * +ValueObject::GetSummaryAsCString () +{ + if (UpdateValueIfNeeded(true) && m_summary_str.empty()) + { + GetSummaryAsCString(GetSummaryFormat().get(), + m_summary_str); + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + +bool +ValueObject::IsCStringContainer(bool check_pointer) +{ + ClangASTType pointee_or_element_clang_type; + const Flags type_flags (GetTypeInfo (&pointee_or_element_clang_type)); + bool is_char_arr_ptr (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + pointee_or_element_clang_type.IsCharType ()); + if (!is_char_arr_ptr) + return false; + if (!check_pointer) + return true; + if (type_flags.Test(ClangASTType::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) +{ + ClangASTType pointee_or_element_clang_type; + const uint32_t type_info = GetTypeInfo (&pointee_or_element_clang_type); + const bool is_pointer_type = type_info & ClangASTType::eTypeIsPointer; + const bool is_array_type = type_info & ClangASTType::eTypeIsArray; + if (!(is_pointer_type || is_array_type)) + return 0; + + if (item_count == 0) + return 0; + + const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(); + 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) + { + Error error; + ValueObjectSP pointee_sp = Dereference(error); + if (error.Fail() || pointee_sp.get() == NULL) + return 0; + return pointee_sp->GetDataExtractor().Copy(data); + } + else + { + ValueObjectSP child_sp = GetChildAtIndex(0, true); + if (child_sp.get() == NULL) + return 0; + return child_sp->GetDataExtractor().Copy(data); + } + return true; + } + else /* (items > 1) */ + { + Error error; + lldb_private::DataBufferHeap* heap_buf_ptr = NULL; + 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()) + { + data.SetData(data_sp); + return bytes_read; + } + } + } + break; + case eAddressTypeHost: + { + const uint64_t max_bytes = GetClangType().GetByteSize(); + if (max_bytes > offset) + { + size_t bytes_read = std::min<uint64_t>(max_bytes - offset, bytes); + 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) +{ + UpdateValueIfNeeded(false); + ExecutionContext exe_ctx (GetExecutionContextRef()); + Error error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); + if (error.Fail()) + { + if (m_data.GetByteSize()) + { + data = m_data; + return data.GetByteSize(); + } + else + { + return 0; + } + } + data.SetAddressByteSize(m_data.GetAddressByteSize()); + data.SetByteOrder(m_data.GetByteOrder()); + return data.GetByteSize(); +} + +bool +ValueObject::SetData (DataExtractor &data, Error &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 = GetClangType().GetEncoding(count); + + const size_t byte_size = GetByteSize(); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeScalar: + { + Error 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; +} + +// will compute strlen(str), but without consuming more than +// maxlen bytes out of str (this serves the purpose of reading +// chunks of a string without having to worry about +// missing NULL terminators in the chunk) +// of course, if strlen(str) > maxlen, the function will return +// maxlen_value (which should be != maxlen, because that allows you +// to know whether strlen(str) == maxlen or strlen(str) > maxlen) +static uint32_t +strlen_or_inf (const char* str, + uint32_t maxlen, + uint32_t maxlen_value) +{ + uint32_t len = 0; + if (str) + { + while(*str) + { + len++;str++; + if (len >= maxlen) + return maxlen_value; + } + } + return len; +} + +size_t +ValueObject::ReadPointedString (Stream& s, + Error& error, + uint32_t max_length, + bool honor_array, + Format item_format) +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target* target = exe_ctx.GetTargetPtr(); + + if (!target) + { + s << "<no target to read from>"; + error.SetErrorString("no target to read from"); + return 0; + } + + if (max_length == 0) + max_length = target->GetMaximumSizeOfStringSummary(); + + size_t bytes_read = 0; + size_t total_bytes_read = 0; + + ClangASTType clang_type = GetClangType(); + ClangASTType elem_or_pointee_clang_type; + const Flags type_flags (GetTypeInfo (&elem_or_pointee_clang_type)); + if (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + elem_or_pointee_clang_type.IsCharType ()) + { + addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + + size_t cstr_len = 0; + bool capped_data = false; + if (type_flags.Test (ClangASTType::eTypeIsArray)) + { + // We have an array + uint64_t array_size = 0; + if (clang_type.IsArrayType(NULL, &array_size, NULL)) + { + 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) + { + s << "<invalid address>"; + error.SetErrorString("invalid address"); + return 0; + } + + 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; + s << '"'; + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 1, // Size of item (1 byte for a char!) + bytes_read, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + if (capped_data) + s << "..."; + s << '"'; + } + } + 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 = strlen_or_inf (cstr, k_max_buf_size, k_max_buf_size+1); + if (len > k_max_buf_size) + len = k_max_buf_size; + if (cstr && cstr_len_displayed < 0) + s << '"'; + + 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; + + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 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 + + 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) + { + s << '"'; + if (capped_cstr) + s << "..."; + } + } + } + else + { + error.SetErrorString("not a string object"); + s << "<not a string object>"; + } + return total_bytes_read; +} + +const char * +ValueObject::GetObjectDescription () +{ + + if (!UpdateValueIfNeeded (true)) + return NULL; + + if (!m_object_desc_str.empty()) + return m_object_desc_str.c_str(); + + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return NULL; + + StreamString s; + + LanguageType language = GetObjectRuntimeLanguage(); + LanguageRuntime *runtime = process->GetLanguageRuntime(language); + + if (runtime == NULL) + { + // Aw, hell, if the things a pointer, or even just an integer, let's try ObjC anyway... + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + bool is_signed; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType ()) + { + runtime = process->GetLanguageRuntime(eLanguageTypeObjC); + } + } + } + + if (runtime && runtime->GetObjectDescription(s, *this)) + { + m_object_desc_str.append (s.GetData()); + } + + if (m_object_desc_str.empty()) + return NULL; + else + return m_object_desc_str.c_str(); +} + +bool +ValueObject::GetValueAsCString (lldb::Format format, + std::string& destination) +{ + if (GetClangType().IsAggregateType () == false && UpdateValueIfNeeded(false)) + { + const Value::ContextType context_type = m_value.GetContextType(); + + if (context_type == Value::eContextTypeRegisterInfo) + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + StreamString reg_sstr; + m_data.Dump (®_sstr, + 0, + format, + reg_info->byte_size, + 1, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + 0, + 0, + exe_ctx.GetBestExecutionContextScope()); + destination.swap(reg_sstr.GetString()); + } + } + else + { + ClangASTType clang_type = GetClangType (); + if (clang_type) + { + // put custom bytes to display in this DataExtractor to override the default value logic + lldb_private::DataExtractor special_format_data; + if (format == eFormatCString) + { + Flags type_flags(clang_type.GetTypeInfo(NULL)); + if (type_flags.Test(ClangASTType::eTypeIsPointer) && !type_flags.Test(ClangASTType::eTypeIsObjC)) + { + // if we are dumping a pointer as a c-string, get the pointee data as a string + TargetSP target_sp(GetTargetSP()); + if (target_sp) + { + size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); + Error error; + DataBufferSP buffer_sp(new DataBufferHeap(max_len+1,0)); + Address address(GetPointerValue()); + if (target_sp->ReadCStringFromMemory(address, (char*)buffer_sp->GetBytes(), max_len, error) && error.Success()) + special_format_data.SetData(buffer_sp); + } + } + } + + StreamString sstr; + ExecutionContext exe_ctx (GetExecutionContextRef()); + clang_type.DumpTypeValue (&sstr, // The stream to use for display + format, // Format to display this type with + special_format_data.GetByteSize() ? + special_format_data: m_data, // Data to extract from + 0, // Byte offset into "m_data" + GetByteSize(), // Byte size of item in "m_data" + GetBitfieldBitSize(), // Bitfield bit size + GetBitfieldBitOffset(), // Bitfield bit offset + exe_ctx.GetBestExecutionContextScope()); + // Don't set the m_error to anything here otherwise + // we won't be able to re-format as anything else. The + // code for ClangASTType::DumpTypeValue() should always + // return something, even if that something contains + // an error messsage. "m_error" is used to detect errors + // when reading the valid object, not for formatting errors. + if (sstr.GetString().empty()) + destination.clear(); + else + destination.swap(sstr.GetString()); + } + } + return !destination.empty(); + } + else + return false; +} + +const char * +ValueObject::GetValueAsCString () +{ + if (UpdateValueIfNeeded(true)) + { + lldb::Format my_format = GetFormat(); + if (my_format == lldb::eFormatDefault) + { + if (m_type_format_sp) + my_format = m_type_format_sp->GetFormat(); + 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 = GetClangType().GetFormat(); + } + } + } + } + if (my_format != m_last_format || m_value_str.empty()) + { + m_last_format = my_format; + if (GetValueAsCString(my_format, 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 NULL; + 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 (!GetClangType().IsAggregateType()) + { + Scalar scalar; + if (ResolveValue (scalar)) + { + if (success) + *success = true; + return scalar.ULongLong(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(ClangASTType::eTypeIsArray | ClangASTType::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(ClangASTType::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) +{ + + Flags flags(GetTypeInfo()); + + bool allow_special = ((special & ePrintableRepresentationSpecialCasesAllow) == ePrintableRepresentationSpecialCasesAllow); + bool only_special = ((special & ePrintableRepresentationSpecialCasesOnly) == ePrintableRepresentationSpecialCasesOnly); + + if (allow_special) + { + if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::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 + { + Error error; + ReadPointedString(s, + error, + 0, + (custom_format == eFormatVectorOfChar) || + (custom_format == eFormatCharArray)); + 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(ClangASTType::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; + + { + const char *cstr = NULL; + + // 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: + cstr = GetValueAsCString(); + break; + + case eValueObjectRepresentationStyleSummary: + cstr = GetSummaryAsCString(); + break; + + case eValueObjectRepresentationStyleLanguageSpecific: + cstr = GetObjectDescription(); + break; + + case eValueObjectRepresentationStyleLocation: + cstr = GetLocationAsCString(); + break; + + case eValueObjectRepresentationStyleChildrenCount: + strm.Printf("%zu", GetNumChildren()); + cstr = strm.GetString().c_str(); + break; + + case eValueObjectRepresentationStyleType: + cstr = GetTypeName().AsCString(); + break; + + case eValueObjectRepresentationStyleName: + cstr = GetName().AsCString(); + break; + + case eValueObjectRepresentationStyleExpressionPath: + GetExpressionPath(strm, false); + cstr = strm.GetString().c_str(); + break; + } + + if (!cstr) + { + if (val_obj_display == eValueObjectRepresentationStyleValue) + cstr = GetSummaryAsCString(); + else if (val_obj_display == eValueObjectRepresentationStyleSummary) + { + if (GetClangType().IsAggregateType()) + { + strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString()); + cstr = strm.GetString().c_str(); + } + else + cstr = GetValueAsCString(); + } + } + + if (cstr) + s.PutCString(cstr); + else + { + if (m_error.Fail()) + s.Printf("<%s>", m_error.AsCString()); + 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) +{ + 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: + case Value::eValueTypeHostAddress: + { + if(address_type) + *address_type = m_value.GetValueAddressType (); + return m_value.GetScalar().ULongLong(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, Error& 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 = GetClangType().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 <= Scalar::GetMaxByteSize()) + { + // 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 GetClangType().GetConstTypeName(); +} + +ConstString +ValueObject::GetQualifiedTypeName() +{ + return GetClangType().GetConstQualifiedTypeName(); +} + + +LanguageType +ValueObject::GetObjectRuntimeLanguage () +{ + return GetClangType().GetMinimumLanguage (); +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObject *valobj) +{ + m_synthetic_children[key] = valobj; +} + +ValueObjectSP +ValueObject::GetSyntheticChild (const 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 (ClangASTType *pointee_or_element_clang_type) +{ + return GetClangType().GetTypeInfo (pointee_or_element_clang_type); +} + +bool +ValueObject::IsPointerType () +{ + return GetClangType().IsPointerType(); +} + +bool +ValueObject::IsArrayType () +{ + return GetClangType().IsArrayType (NULL, NULL, NULL); +} + +bool +ValueObject::IsScalarType () +{ + return GetClangType().IsScalarType (); +} + +bool +ValueObject::IsIntegerType (bool &is_signed) +{ + return GetClangType().IsIntegerType (is_signed); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return GetClangType().IsPointerOrReferenceType (); +} + +bool +ValueObject::IsPossibleDynamicType () +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + return process->IsPossibleDynamicValue(*this); + else + return GetClangType().IsPossibleDynamicType (NULL, true, true); +} + +bool +ValueObject::IsObjCNil () +{ + const uint32_t mask = ClangASTType::eTypeIsObjC | ClangASTType::eTypeIsPointer; + bool isObjCpointer = (((GetClangType().GetTypeInfo(NULL)) & mask) == mask); + if (!isObjCpointer) + return false; + bool canReadValue = true; + bool isZero = GetValueAsUnsigned(0,&canReadValue) == 0; + return canReadValue && isZero; +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) +{ + const uint32_t type_info = GetTypeInfo (); + if (type_info & ClangASTType::eTypeIsArray) + return GetSyntheticArrayMemberFromArray(index, can_create); + + if (type_info & ClangASTType::eTypeIsPointer) + return GetSyntheticArrayMemberFromPointer(index, can_create); + + return ValueObjectSP(); + +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", 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; +} + +// 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::GetSyntheticArrayMemberFromArray (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsArrayType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", 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) + { + // 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, + GetClangType(), + index_const_str, + GetByteSize(), + 0, + to-from+1, + from, + false, + false, + eAddressTypeInvalid); + + // 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 ClangASTType& type, bool can_create) +{ + + ValueObjectSP synthetic_child_sp; + + char name_str[64]; + snprintf(name_str, sizeof(name_str), "@%i", offset); + ConstString name_const_str(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 ValueObjectSP(); + + ValueObjectChild *synthetic_child = new ValueObjectChild(*this, + type, + name_const_str, + type.GetByteSize(), + offset, + 0, + 0, + false, + false, + eAddressTypeInvalid); + 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; +} + +// 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, + NULL, NULL, NULL, + GetValueForExpressionPathOptions().DontAllowSyntheticChildren()); + + // 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 == false) + return; + + TargetSP target_sp(GetTargetSP()); + if (target_sp && (target_sp->GetEnableSyntheticValue() == false || target_sp->GetSuppressSyntheticValue() == true)) + { + m_synthetic_value = NULL; + return; + } + + lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp); + + if (!UpdateFormatsIfNeeded() && m_synthetic_value) + return; + + if (m_synthetic_children_sp.get() == NULL) + 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 == NULL) + { + 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 == false) + 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() == NULL) + return false; + + CalculateSyntheticValue(true); + + if (m_synthetic_value) + return true; + else + return false; +} + +bool +ValueObject::GetBaseClassPath (Stream &s) +{ + if (IsBaseClass()) + { + bool parent_had_base_class = GetParent() && GetParent()->GetBaseClassPath (s); + ClangASTType clang_type = GetClangType(); + std::string cxx_class_name; + bool this_had_base_class = clang_type.GetCXXClassName (cxx_class_name); + if (this_had_base_class) + { + if (parent_had_base_class) + s.PutCString("::"); + s.PutCString(cxx_class_name.c_str()); + } + 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 NULL; +} + +void +ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) +{ + 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) + { + ClangASTType non_base_class_parent_clang_type = non_base_class_parent->GetClangType(); + if (non_base_class_parent_clang_type) + { + if (parent && parent->IsDereferenceOfParent() && epformat == eGetExpressionPathFormatHonorPointers) + { + s.PutCString("->"); + } + else + { + const uint32_t non_base_class_parent_type_info = non_base_class_parent_clang_type.GetTypeInfo(); + + if (non_base_class_parent_type_info & ClangASTType::eTypeIsPointer) + { + s.PutCString("->"); + } + else if ((non_base_class_parent_type_info & ClangASTType::eTypeHasChildren) && + !(non_base_class_parent_type_info & ClangASTType::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(const char* expression, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + + const char* dummy_first_unparsed; + 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, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + 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) + { + Error 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) + { + Error 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 +} + +int +ValueObject::GetValuesForExpressionPath(const char* expression, + ValueObjectListSP& list, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + const char* dummy_first_unparsed; + ExpressionPathScanEndReason dummy_reason_to_stop; + ExpressionPathEndResultType dummy_final_value_type; + ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + + ValueObjectSP ret_val = GetValueForExpressionPath_Impl(expression, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + 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 (!ret_val.get()) // if there are errors, I add nothing to the list + return 0; + + if ( (reason_to_stop ? *reason_to_stop : dummy_reason_to_stop) != eExpressionPathScanEndReasonArrayRangeOperatorMet) + { + // I need not expand a range, just post-process the final value and return + if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) + { + list->Append(ret_val); + return 1; + } + 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 == ValueObject::eExpressionPathAftermathDereference) + { + Error 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 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + if (*final_task_on_target == ValueObject::eExpressionPathAftermathTakeAddress) + { + Error 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 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + } + } + else + { + return ExpandArraySliceExpression(first_unparsed ? *first_unparsed : dummy_first_unparsed, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + ret_val, + list, + 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); + } + // in any non-covered case, just do the obviously right thing + list->Append(ret_val); + return 1; +} + +ValueObjectSP +ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + ValueObjectSP root = GetSP(); + + if (!root.get()) + return ValueObjectSP(); + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + return root; + } + + switch (*expression_cstr) + { + case '-': + { + if (options.m_check_dot_vs_arrow_syntax && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && + options.m_no_fragile_ivar) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (expression_cstr[1] != '>') + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip the - + } + case '.': // or fallthrough from -> + { + if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip . + const char *next_separator = strpbrk(expression_cstr+1,"-.["); + ConstString child_name; + if (!next_separator) // if no other separator just expand this last layer + { + child_name.SetCString (expression_cstr); + ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); + + if (child_valobj_sp.get()) // we know we are done, so just return + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // 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 + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // other layers do expand + { + child_name.SetCStringWithLength(expression_cstr, next_separator - expression_cstr); + 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; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(true); + if (child_valobj_sp) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // 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; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + break; + } + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && !root_clang_type_info.Test(ClangASTType::eTypeIsVector)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar... + { + if (options.m_no_synthetic_children) // ...only chance left is synthetic + { + *first_unparsed = expression_cstr; + *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 + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // even if something follows, we cannot expand unbounded ranges, just let the caller do it + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); + if (!child_valobj_sp) + child_valobj_sp = root->GetSyntheticArrayMemberFromArray(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; + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + if (root->GetClangType().GetMinimumLanguage() == eLanguageTypeObjC + && pointee_clang_type_info.AllClear(ClangASTType::eTypeIsPointer) + && root->HasSyntheticValue() + && options.m_no_synthetic_children == false) + { + root = root->GetSyntheticValue()->GetChildAtIndex(index, true); + } + else + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsVector)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else if (options.m_no_synthetic_children == false) + { + if (root->HasSyntheticValue()) + root = root->GetSyntheticValue(); + else if (!root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + // if we are here, then root itself is a synthetic VO.. should be good to go + + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange; + return root; + } + } + break; + } + default: // some non-separator is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + break; + } + } + } +} + +int +ValueObject::ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + ValueObjectSP root, + ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + if (!root.get()) + return 0; + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + list->Append(root); + return 1; + } + + switch (*expression_cstr) + { + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else if (!options.m_allow_bitfields_syntax) // if this is a scalar, check that we can expand bitfields + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // expand this into list + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else /*if (ClangASTContext::IsScalarType(root_clang_type))*/ + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + for (unsigned long index = index_lower; + index <= index_higher; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = end+1; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return index_higher-index_lower+1; // tell me number of items I added to the VOList + } + } + break; + } + default: // some non-[ separator, or something entirely wrong, is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + break; + } + } + } +} + +static void +DumpValueObject_Impl (Stream &s, + ValueObject *valobj, + const ValueObject::DumpValueObjectOptions& options, + uint32_t ptr_depth, + uint32_t curr_depth) +{ + if (valobj) + { + bool update_success = valobj->UpdateValueIfNeeded (true); + + const char *root_valobj_name = + options.m_root_valobj_name.empty() ? + valobj->GetName().AsCString() : + options.m_root_valobj_name.c_str(); + + if (update_success && options.m_use_dynamic != eNoDynamicValues) + { + ValueObject *dynamic_value = valobj->GetDynamicValue(options.m_use_dynamic).get(); + if (dynamic_value) + valobj = dynamic_value; + } + + ClangASTType clang_type = valobj->GetClangType(); + const Flags type_flags (clang_type.GetTypeInfo ()); + const char *err_cstr = NULL; + const bool has_children = type_flags.Test (ClangASTType::eTypeHasChildren); + const bool has_value = type_flags.Test (ClangASTType::eTypeHasValue); + + const bool print_valobj = options.m_flat_output == false || has_value; + + if (print_valobj) + { + if (options.m_show_location) + { + s.Printf("%s: ", valobj->GetLocationAsCString()); + } + + s.Indent(); + + bool show_type = true; + // if we are at the root-level and been asked to hide the root's type, then hide it + if (curr_depth == 0 && options.m_hide_root_type) + show_type = false; + else + // otherwise decide according to the usual rules (asked to show types - always at the root level) + show_type = options.m_show_types || (curr_depth == 0 && !options.m_flat_output); + + if (show_type) + { + // Some ValueObjects don't have types (like registers sets). Only print + // the type if there is one to print + ConstString qualified_type_name(valobj->GetQualifiedTypeName()); + if (qualified_type_name) + s.Printf("(%s) ", qualified_type_name.GetCString()); + } + + if (options.m_flat_output) + { + // If we are showing types, also qualify the C++ base classes + const bool qualify_cxx_base_classes = options.m_show_types; + if (!options.m_hide_name) + { + valobj->GetExpressionPath(s, qualify_cxx_base_classes); + s.PutCString(" ="); + } + } + else if (!options.m_hide_name) + { + const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); + s.Printf ("%s =", name_cstr); + } + + if (!options.m_scope_already_checked && !valobj->IsInScope()) + { + err_cstr = "out of scope"; + } + } + + std::string summary_str; + std::string value_str; + const char *val_cstr = NULL; + const char *sum_cstr = NULL; + TypeSummaryImpl* entry = options.m_summary_sp ? options.m_summary_sp.get() : valobj->GetSummaryFormat().get(); + + if (options.m_omit_summary_depth > 0) + entry = NULL; + + bool is_nil = valobj->IsObjCNil(); + + if (err_cstr == NULL) + { + if (options.m_format != eFormatDefault && options.m_format != valobj->GetFormat()) + { + valobj->GetValueAsCString(options.m_format, + value_str); + } + else + { + val_cstr = valobj->GetValueAsCString(); + if (val_cstr) + value_str = val_cstr; + } + err_cstr = valobj->GetError().AsCString(); + } + + if (err_cstr) + { + s.Printf (" <%s>\n", err_cstr); + } + else + { + const bool is_ref = type_flags.Test (ClangASTType::eTypeIsReference); + if (print_valobj) + { + if (is_nil) + sum_cstr = "nil"; + else if (options.m_omit_summary_depth == 0) + { + if (options.m_summary_sp) + { + valobj->GetSummaryAsCString(entry, summary_str); + sum_cstr = summary_str.c_str(); + } + else + sum_cstr = valobj->GetSummaryAsCString(); + } + + // Make sure we have a value and make sure the summary didn't + // specify that the value should not be printed - and do not print + // the value if this thing is nil + // (but show the value if the user passes a format explicitly) + if (!is_nil && !value_str.empty() && (entry == NULL || (entry->DoesPrintValue() || options.m_format != eFormatDefault) || sum_cstr == NULL) && !options.m_hide_value) + s.Printf(" %s", value_str.c_str()); + + if (sum_cstr) + s.Printf(" %s", sum_cstr); + + // let's avoid the overly verbose no description error for a nil thing + if (options.m_use_objc && !is_nil) + { + if (!options.m_hide_value || !options.m_hide_name) + s.Printf(" "); + const char *object_desc = valobj->GetObjectDescription(); + if (object_desc) + s.Printf("%s\n", object_desc); + else + s.Printf ("[no Objective-C description available]\n"); + return; + } + } + + if (curr_depth < options.m_max_depth) + { + // We will show children for all concrete types. We won't show + // pointer contents unless a pointer depth has been specified. + // We won't reference contents unless the reference is the + // root object (depth of zero). + bool print_children = true; + + // Use a new temporary pointer depth in case we override the + // current pointer depth below... + uint32_t curr_ptr_depth = ptr_depth; + + const bool is_ptr = type_flags.Test (ClangASTType::eTypeIsPointer); + if (is_ptr || is_ref) + { + // We have a pointer or reference whose value is an address. + // Make sure that address is not NULL + AddressType ptr_address_type; + if (valobj->GetPointerValue (&ptr_address_type) == 0) + print_children = false; + + else if (is_ref && curr_depth == 0) + { + // If this is the root object (depth is zero) that we are showing + // and it is a reference, and no pointer depth has been supplied + // print out what it references. Don't do this at deeper depths + // otherwise we can end up with infinite recursion... + curr_ptr_depth = 1; + } + + if (curr_ptr_depth == 0) + print_children = false; + } + + if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) + { + ValueObjectSP synth_valobj_sp = valobj->GetSyntheticValue (options.m_use_synthetic); + ValueObject* synth_valobj = (synth_valobj_sp ? synth_valobj_sp.get() : valobj); + + size_t num_children = synth_valobj->GetNumChildren(); + bool print_dotdotdot = false; + if (num_children) + { + if (options.m_flat_output) + { + if (print_valobj) + s.EOL(); + } + else + { + if (print_valobj) + s.PutCString(is_ref ? ": {\n" : " {\n"); + s.IndentMore(); + } + + const size_t max_num_children = valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + if (num_children > max_num_children && !options.m_ignore_cap) + { + num_children = max_num_children; + print_dotdotdot = true; + } + + ValueObject::DumpValueObjectOptions child_options(options); + child_options.SetFormat(options.m_format).SetSummary().SetRootValueObjectName(); + child_options.SetScopeChecked(true).SetHideName(options.m_hide_name).SetHideValue(options.m_hide_value) + .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 ? child_options.m_omit_summary_depth - 1 : 0); + for (size_t idx=0; idx<num_children; ++idx) + { + ValueObjectSP child_sp(synth_valobj->GetChildAtIndex(idx, true)); + if (child_sp.get()) + { + DumpValueObject_Impl (s, + child_sp.get(), + child_options, + (is_ptr || is_ref) ? curr_ptr_depth - 1 : curr_ptr_depth, + curr_depth + 1); + } + } + + if (!options.m_flat_output) + { + if (print_dotdotdot) + { + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + target->GetDebugger().GetCommandInterpreter().ChildrenTruncated(); + s.Indent("...\n"); + } + s.IndentLess(); + s.Indent("}\n"); + } + } + else if (has_children) + { + // Aggregate, no children... + if (print_valobj) + s.PutCString(" {}\n"); + } + else + { + if (print_valobj) + s.EOL(); + } + + } + else + { + s.EOL(); + } + } + else + { + if (has_children && print_valobj) + { + s.PutCString("{...}\n"); + } + } + } + } +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj) +{ + if (log && valobj) + return LogValueObject (log, valobj, DumpValueObjectOptions::DefaultOptions()); +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + if (log && valobj) + { + StreamString s; + ValueObject::DumpValueObject (s, valobj, options); + if (s.GetSize()) + log->PutCString(s.GetData()); + } +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj) +{ + + if (!valobj) + return; + + DumpValueObject_Impl(s, + valobj, + DumpValueObjectOptions::DefaultOptions(), + 0, + 0); +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + DumpValueObject_Impl(s, + valobj, + options, + options.m_max_ptr_depth, // max pointer depth allowed, we will go down from here + 0 // current object depth is 0 since we are just starting + ); +} + +ValueObjectSP +ValueObject::CreateConstantValue (const 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, 0, GetModule().get()); + } + else + m_error = m_value.GetValueAsData (&exe_ctx, data, 0, GetModule().get()); + + valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + GetClangType(), + name, + data, + GetAddressOf()); + } + + if (!valobj_sp) + { + valobj_sp = ValueObjectConstResult::Create (NULL, m_error); + } + return valobj_sp; +} + +ValueObjectSP +ValueObject::Dereference (Error &error) +{ + if (m_deref_valobj) + return m_deref_valobj->GetSP(); + + const bool is_pointer_type = IsPointerType(); + if (is_pointer_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; + ClangASTType clang_type = GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 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); + if (child_clang_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_clang_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); + } + } + + if (m_deref_valobj) + { + error.Clear(); + return m_deref_valobj->GetSP(); + } + else + { + StreamString strm; + GetExpressionPath(strm, true); + + if (is_pointer_type) + error.SetErrorStringWithFormat("dereference failed: (%s) %s", GetTypeName().AsCString("<invalid type>"), strm.GetString().c_str()); + else + error.SetErrorStringWithFormat("not a pointer type: (%s) %s", GetTypeName().AsCString("<invalid type>"), strm.GetString().c_str()); + return ValueObjectSP(); + } +} + +ValueObjectSP +ValueObject::AddressOf (Error &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) + { + switch (address_type) + { + case eAddressTypeInvalid: + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_strm.GetString().c_str()); + } + break; + + case eAddressTypeFile: + case eAddressTypeLoad: + case eAddressTypeHost: + { + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + std::string name (1, '&'); + name.append (m_name.AsCString("")); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_addr_of_valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + clang_type.GetPointerType(), + ConstString (name.c_str()), + addr, + eAddressTypeInvalid, + m_data.GetAddressByteSize()); + } + } + break; + } + } + else + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' doesn't have a valid address", expr_path_strm.GetString().c_str()); + } + + return m_addr_of_valobj_sp; +} + +ValueObjectSP +ValueObject::Cast (const ClangASTType &clang_ast_type) +{ + return ValueObjectCast::Create (*this, GetName(), clang_ast_type); +} + +ValueObjectSP +ValueObject::CastPointerType (const char *name, ClangASTType &clang_ast_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, + clang_ast_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), + m_first_update (true) +{ +} + +ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected): + m_mod_id(), + m_exe_ctx_ref(), + m_needs_update (true), + m_first_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), + m_first_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() +{ + + // Start with the target, if it is NULL, then we're obviously not going to get any further: + ExecutionContext exe_ctx(m_exe_ctx_ref.Lock()); + + if (exe_ctx.GetTargetPtr() == NULL) + return false; + + // If we don't have a process nothing can change. + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + 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 (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_first_update = false; + 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 = NULL; + } +} + +SymbolContextScope * +ValueObject::GetSymbolContextScope() +{ + if (m_parent) + { + if (!m_parent->IsPointerOrReferenceType()) + return m_parent->GetSymbolContextScope(); + } + return NULL; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx) +{ + lldb::ValueObjectSP retval_sp; + lldb::TargetSP target_sp(exe_ctx.GetTargetSP()); + if (!target_sp) + return retval_sp; + if (!expression || !*expression) + return retval_sp; + target_sp->EvaluateExpression (expression, + exe_ctx.GetFrameSP().get(), + retval_sp); + if (retval_sp && name && *name) + retval_sp->SetName(ConstString(name)); + return retval_sp; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + if (type) + { + ClangASTType 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, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize())); + if (ptr_result_valobj_sp) + { + ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); + Error err; + ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); + if (ptr_result_valobj_sp && name && *name) + ptr_result_valobj_sp->SetName(ConstString(name)); + } + return ptr_result_valobj_sp; + } + } + return lldb::ValueObjectSP(); +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromData (const char* name, + DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType 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 && *name) + 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; + ValueObject* parent = m_parent; + if (!parent) + return (m_root = this); + while (parent->m_parent) + { + if (parent->m_root) + return (m_root = parent->m_root); + parent = parent->m_parent; + } + return (m_root = parent); +} + +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; +} diff --git a/source/Core/ValueObjectCast.cpp b/source/Core/ValueObjectCast.cpp new file mode 100644 index 0000000..4f4f8cc --- /dev/null +++ b/source/Core/ValueObjectCast.cpp @@ -0,0 +1,129 @@ +//===-- ValueObjectDynamicValue.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectCast.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ClangASTType.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/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +lldb::ValueObjectSP +ValueObjectCast::Create (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type) +{ + ValueObjectCast *cast_valobj_ptr = new ValueObjectCast (parent, name, cast_type); + return cast_valobj_ptr->GetSP(); +} + +ValueObjectCast::ValueObjectCast +( + ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type +) : + ValueObject(parent), + m_cast_type (cast_type) +{ + SetName (name); + //m_value.SetContext (Value::eContextTypeClangType, cast_type.GetOpaqueQualType()); + m_value.SetClangType (cast_type); +} + +ValueObjectCast::~ValueObjectCast() +{ +} + +ClangASTType +ValueObjectCast::GetClangTypeImpl () +{ + return m_cast_type; +} + +size_t +ValueObjectCast::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +uint64_t +ValueObjectCast::GetByteSize() +{ + return m_value.GetValueByteSize(NULL); +} + +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(); + ClangASTType clang_type (GetClangType()); + //m_value.SetContext (Value::eContextTypeClangType, clang_type); + m_value.SetClangType (clang_type); + SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren()); + if (clang_type.IsAggregateType ()) + { + // 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, 0, 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/source/Core/ValueObjectChild.cpp b/source/Core/ValueObjectChild.cpp new file mode 100644 index 0000000..23add1c --- /dev/null +++ b/source/Core/ValueObjectChild.cpp @@ -0,0 +1,234 @@ +//===-- ValueObjectChild.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectChild.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.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/Target.h" + +using namespace lldb_private; + +ValueObjectChild::ValueObjectChild +( + ValueObject &parent, + const ClangASTType &clang_type, + const 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 +) : + ValueObject (parent), + m_clang_type (clang_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_name = name; + SetAddressTypeOfChildren(child_ptr_or_ref_addr_type); +} + +ValueObjectChild::~ValueObjectChild() +{ +} + +lldb::ValueType +ValueObjectChild::GetValueType() const +{ + return m_parent->GetValueType(); +} + +size_t +ValueObjectChild::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +ConstString +ValueObjectChild::GetTypeName() +{ + if (m_type_name.IsEmpty()) + { + m_type_name = GetClangType().GetConstTypeName (); + if (m_type_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = m_type_name.AsCString(); + if (clang_type_name) + { + std::vector<char> bitfield_type_name (strlen(clang_type_name) + 32, 0); + ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", clang_type_name, m_bitfield_bit_size); + m_type_name.SetCString(&bitfield_type_name.front()); + } + } + } + } + return m_type_name; +} + +ConstString +ValueObjectChild::GetQualifiedTypeName() +{ + ConstString qualified_name = GetClangType().GetConstTypeName(); + if (qualified_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = qualified_name.AsCString(); + if (clang_type_name) + { + std::vector<char> bitfield_type_name (strlen(clang_type_name) + 32, 0); + ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", clang_type_name, m_bitfield_bit_size); + qualified_name.SetCString(&bitfield_type_name.front()); + } + } + } + return qualified_name; +} + +bool +ValueObjectChild::UpdateValue () +{ + m_error.Clear(); + SetValueIsValid (false); + ValueObject* parent = m_parent; + if (parent) + { + if (parent->UpdateValueIfNeeded(false)) + { + m_value.SetClangType(GetClangType()); + + // 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); + + if (parent->GetClangType().IsPointerOrReferenceType ()) + { + 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() == true) + m_value.SetValueType (Value::eValueTypeLoadAddress); + else + m_value.SetValueType(Value::eValueTypeFileAddress); + } + break; + case eAddressTypeLoad: + m_value.SetValueType (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(); + } + } + break; + + case Value::eValueTypeScalar: + // TODO: What if this is a register value? Do we try and + // extract the child value from within the parent data? + // Probably... + default: + m_error.SetErrorString ("parent has invalid value."); + break; + } + } + + if (m_error.Success()) + { + ExecutionContext exe_ctx (GetExecutionContextRef().Lock()); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + } + } + 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/source/Core/ValueObjectConstResult.cpp b/source/Core/ValueObjectConstResult.cpp new file mode 100644 index 0000000..d6d8638 --- /dev/null +++ b/source/Core/ValueObjectConstResult.cpp @@ -0,0 +1,354 @@ +//===-- ValueObjectConstResult.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResult.h" + +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResultChild.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.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/Target.h" + +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 ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address +) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + data, + address))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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.SetClangType(clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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, + clang_type, + name, + data_sp, + data_byte_order, + data_addr_size, + address))->GetSP(); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + Value &value, + const ConstString &name) +{ + return (new ValueObjectConstResult (exe_scope, value, name))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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, clang_type); + m_value.SetClangType (clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + address, + address_type, + addr_byte_size))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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, clang_type); + m_value.SetClangType (clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create +( + ExecutionContextScope *exe_scope, + const Error& error +) +{ + return (new ValueObjectConstResult (exe_scope, + error))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Error& 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, + const ConstString &name) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this) +{ + m_value = value; + m_value.GetData(m_data); +} + +ValueObjectConstResult::~ValueObjectConstResult() +{ +} + +ClangASTType +ValueObjectConstResult::GetClangTypeImpl() +{ + return m_value.GetClangType(); +} + +lldb::ValueType +ValueObjectConstResult::GetValueType() const +{ + return eValueTypeConstResult; +} + +uint64_t +ValueObjectConstResult::GetByteSize() +{ + if (m_byte_size == 0) + m_byte_size = GetClangType().GetByteSize(); + return m_byte_size; +} + +void +ValueObjectConstResult::SetByteSize (size_t size) +{ + m_byte_size = size; +} + +size_t +ValueObjectConstResult::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +ConstString +ValueObjectConstResult::GetTypeName() +{ + if (m_type_name.IsEmpty()) + m_type_name = GetClangType().GetConstTypeName (); + return m_type_name; +} + +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 (Error &error) +{ + return m_impl.Dereference(error); +} + +lldb::ValueObjectSP +ValueObjectConstResult::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + return m_impl.GetSyntheticChildAtOffset(offset, type, can_create); +} + +lldb::ValueObjectSP +ValueObjectConstResult::AddressOf (Error &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(); +} + diff --git a/source/Core/ValueObjectConstResultChild.cpp b/source/Core/ValueObjectConstResultChild.cpp new file mode 100644 index 0000000..64425ea --- /dev/null +++ b/source/Core/ValueObjectConstResultChild.cpp @@ -0,0 +1,80 @@ +//===-- ValueObjectConstResultChild.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResultChild.h" + +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTContext.h" + +using namespace lldb_private; + +ValueObjectConstResultChild::ValueObjectConstResultChild +( + ValueObject &parent, + const ClangASTType &clang_type, + const 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 +) : + ValueObjectChild (parent, + clang_type, + name, + byte_size, + byte_offset, + bitfield_bit_size, + bitfield_bit_offset, + is_base_class, + is_deref_of_parent, + eAddressTypeLoad), + m_impl(this) +{ + m_name = name; +} + +ValueObjectConstResultChild::~ValueObjectConstResultChild() +{ +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::Dereference (Error &error) +{ + return m_impl.Dereference(error); +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + return m_impl.GetSyntheticChildAtOffset(offset, type, can_create); +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::AddressOf (Error &error) +{ + return m_impl.AddressOf(error); +} + +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); +} diff --git a/source/Core/ValueObjectConstResultImpl.cpp b/source/Core/ValueObjectConstResultImpl.cpp new file mode 100644 index 0000000..e0757f6 --- /dev/null +++ b/source/Core/ValueObjectConstResultImpl.cpp @@ -0,0 +1,236 @@ +//===-- ValueObjectConstResultImpl.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResultImpl.h" + +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectConstResultChild.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.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/Target.h" + +using namespace lldb; +using namespace lldb_private; + +// this macro enables a simpler implementation for some method calls in this object that relies only upon +// ValueObject knowning how to set the address type of its children correctly. the alternative implementation +// relies on being able to create a target copy of the frozen object, which makes it less bug-prone but less +// efficient as well. once we are confident the faster implementation is bug-free, this macro (and the slower +// implementations) can go +#define TRIVIAL_IMPL 1 + +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::DerefOnTarget() +{ + if (m_load_addr_backend.get() == NULL) + { + lldb::addr_t tgt_address = m_impl_backend->GetPointerValue(); + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + m_load_addr_backend = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + m_impl_backend->GetClangType(), + m_impl_backend->GetName(), + tgt_address, + eAddressTypeLoad, + exe_ctx.GetAddressByteSize()); + } + return m_load_addr_backend; +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::Dereference (Error &error) +{ + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::Dereference(error); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget()) + return DerefOnTarget()->Dereference(error); + else + return m_impl_backend->ValueObject::Dereference(error); +#endif +} + +ValueObject * +ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + if (m_impl_backend == NULL) + return NULL; + + m_impl_backend->UpdateValueIfNeeded(false); + + ValueObjectConstResultChild *valobj = NULL; + + 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; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType clang_type = m_impl_backend->GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + m_impl_backend->GetName().GetCString(), + 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); + if (child_clang_type && child_byte_size) + { + 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_clang_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); + valobj->m_impl.SetLiveAddress(m_live_address+child_byte_offset); + } + + return valobj; +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::GetSyntheticChildAtOffset (uint32_t offset, const ClangASTType& type, bool can_create) +{ + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget()) + return DerefOnTarget()->GetSyntheticChildAtOffset(offset, type, can_create); + else + return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); +#endif +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::AddressOf (Error &error) +{ + if (m_address_of_backend.get() != NULL) + return m_address_of_backend; + + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + if (m_live_address != LLDB_INVALID_ADDRESS) + { + ClangASTType clang_type(m_impl_backend->GetClangType()); + + 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(), + clang_type.GetPointerType(), + ConstString(new_name.c_str()), + buffer, + lldb::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 lldb::ValueObjectSP(); +} + +lldb::addr_t +ValueObjectConstResultImpl::GetAddressOf (bool scalar_is_load_address, + AddressType *address_type) +{ + + if (m_impl_backend == NULL) + 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 == NULL) + return 0; +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget() && m_impl_backend->IsPointerType()) + return DerefOnTarget()->GetPointeeData(data, item_idx, item_count); + else + return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); +#endif +} diff --git a/source/Core/ValueObjectDynamicValue.cpp b/source/Core/ValueObjectDynamicValue.cpp new file mode 100644 index 0000000..977cc4c --- /dev/null +++ b/source/Core/ValueObjectDynamicValue.cpp @@ -0,0 +1,372 @@ +//===-- ValueObjectDynamicValue.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectDynamicValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ClangASTType.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/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +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(); +} + +ClangASTType +ValueObjectDynamicValue::GetClangTypeImpl () +{ + if (m_dynamic_type_info.HasTypeSP()) + return m_value.GetClangType(); + else + return m_parent->GetClangType(); +} + +ConstString +ValueObjectDynamicValue::GetTypeName() +{ + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetConstTypeName(); + if (m_dynamic_type_info.HasName()) + return m_dynamic_type_info.GetName(); + } + return m_parent->GetTypeName(); +} + +ConstString +ValueObjectDynamicValue::GetQualifiedTypeName() +{ + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetConstQualifiedTypeName (); + if (m_dynamic_type_info.HasName()) + return m_dynamic_type_info.GetName(); + } + return m_parent->GetTypeName(); +} + +size_t +ValueObjectDynamicValue::CalculateNumChildren() +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetNumChildren (true); + else + return m_parent->GetNumChildren(); +} + +uint64_t +ValueObjectDynamicValue::GetByteSize() +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_dynamic_type_info.HasTypeSP()) + return m_value.GetValueByteSize(NULL); + 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; + + lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage(); + if (known_type != lldb::eLanguageTypeUnknown && known_type != lldb::eLanguageTypeC) + { + LanguageRuntime *runtime = process->GetLanguageRuntime (known_type); + if (runtime) + found_dynamic_type = runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + } + else + { + LanguageRuntime *cpp_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeC_plus_plus); + if (cpp_runtime) + found_dynamic_type = cpp_runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + + if (!found_dynamic_type) + { + LanguageRuntime *objc_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeObjC); + if (objc_runtime) + found_dynamic_type = objc_runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + } + } + + // 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 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, 0, 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; + } + + ClangASTType corrected_type; + if (m_dynamic_type_info.HasTypeSP()) + { + // The type will always be the type of the dynamic object. If our parent's type was a pointer, + // then our type should be a pointer to the type of the dynamic object. If a reference, then the original type + // should be okay... + ClangASTType orig_type = m_dynamic_type_info.GetTypeSP()->GetClangForwardType(); + corrected_type = orig_type; + if (m_parent->IsPointerType()) + corrected_type = orig_type.GetPointerType (); + else if (m_parent->IsPointerOrReferenceType()) + corrected_type = orig_type.GetLValueReferenceType (); + } + else /*if (m_dynamic_type_info.HasName())*/ + { + // If we are here we need to adjust our dynamic type name to include the correct & or * symbol + std::string type_name_buf (m_dynamic_type_info.GetName().GetCString()); + if (m_parent->IsPointerType()) + type_name_buf.append(" *"); + else if (m_parent->IsPointerOrReferenceType()) + type_name_buf.append(" &"); + corrected_type = m_parent->GetClangType(); + m_dynamic_type_info.SetName(type_name_buf.c_str()); + } + + //m_value.SetContext (Value::eContextTypeClangType, corrected_type); + m_value.SetClangType (corrected_type); + + // Our address is the location of the dynamic type stored in memory. It isn't a load address, + // because we aren't pointing to the LOCATION that stores the pointer to us, we're pointing to us... + m_value.SetValueType(Value::eValueTypeScalar); + + if (has_changed_type && log) + log->Printf("[%s %p] has a new dynamic type %s", + GetName().GetCString(), + 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, 0, GetModule().get()); + if (m_error.Success()) + { + if (GetClangType().IsAggregateType ()) + { + // 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, Error& 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, Error &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; +} diff --git a/source/Core/ValueObjectList.cpp b/source/Core/ValueObjectList.cpp new file mode 100644 index 0000000..180d4a0 --- /dev/null +++ b/source/Core/ValueObjectList.cpp @@ -0,0 +1,166 @@ +//===-- ValueObjectList.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectList::ValueObjectList () : + m_value_objects() +{ +} + +ValueObjectList::ValueObjectList (const ValueObjectList &rhs) : + m_value_objects(rhs.m_value_objects) +{ +} + + +ValueObjectList::~ValueObjectList () +{ +} + +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/source/Core/ValueObjectMemory.cpp b/source/Core/ValueObjectMemory.cpp new file mode 100644 index 0000000..42fd0e8 --- /dev/null +++ b/source/Core/ValueObjectMemory.cpp @@ -0,0 +1,275 @@ +//===-- ValueObjectMemory.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.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/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectSP +ValueObjectMemory::Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp) +{ + return (new ValueObjectMemory (exe_scope, name, address, type_sp))->GetSP(); +} + +ValueObjectSP +ValueObjectMemory::Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type) +{ + return (new ValueObjectMemory (exe_scope, name, address, ast_type))->GetSP(); +} + +ValueObjectMemory::ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp) : + ValueObject(exe_scope), + m_address (address), + m_type_sp(type_sp), + m_clang_type() +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_type_sp.get() != NULL); + 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, + const char *name, + const Address &address, + const ClangASTType &ast_type) : + ValueObject(exe_scope), + m_address (address), + m_type_sp(), + m_clang_type(ast_type) +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_clang_type.GetASTContext()); + assert (m_clang_type.GetOpaqueQualType()); + + TargetSP target_sp (GetTargetSP()); + + SetName (ConstString(name)); +// m_value.SetContext(Value::eContextTypeClangType, m_clang_type.GetOpaqueQualType()); + m_value.SetClangType(m_clang_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() +{ +} + +ClangASTType +ValueObjectMemory::GetClangTypeImpl () +{ + if (m_type_sp) + return m_type_sp->GetClangForwardType(); + return m_clang_type; +} + +ConstString +ValueObjectMemory::GetTypeName() +{ + if (m_type_sp) + return m_type_sp->GetName(); + return m_clang_type.GetConstTypeName(); +} + +size_t +ValueObjectMemory::CalculateNumChildren() +{ + if (m_type_sp) + return m_type_sp->GetNumChildren(true); + const bool omit_empty_base_classes = true; + return m_clang_type.GetNumChildren (omit_empty_base_classes); +} + +uint64_t +ValueObjectMemory::GetByteSize() +{ + if (m_type_sp) + return m_type_sp->GetByteSize(); + return m_clang_type.GetByteSize (); +} + +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: + assert(!"Unhandled expression result value kind..."); + break; + + 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, 0, 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 (GetClangType().IsAggregateType()) + { + // 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_clang_type.GetOpaqueQualType()); + value.SetClangType(m_clang_type); + } + + m_error = value.GetValueAsData(&exe_ctx, m_data, 0, 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/source/Core/ValueObjectRegister.cpp b/source/Core/ValueObjectRegister.cpp new file mode 100644 index 0000000..4f21457 --- /dev/null +++ b/source/Core/ValueObjectRegister.cpp @@ -0,0 +1,431 @@ +//===-- ValueObjectRegister.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectRegister.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +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() +{ +} + +ClangASTType +ValueObjectRegisterContext::GetClangTypeImpl () +{ + return ClangASTType(); +} + +ConstString +ValueObjectRegisterContext::GetTypeName() +{ + return ConstString(); +} + +ConstString +ValueObjectRegisterContext::GetQualifiedTypeName() +{ + return ConstString(); +} + +size_t +ValueObjectRegisterContext::CalculateNumChildren() +{ + return m_reg_ctx_sp->GetRegisterSetCount(); +} + +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() == NULL) + { + 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 = NULL; + + 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 (NULL), + 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() +{ +} + +ClangASTType +ValueObjectRegisterSet::GetClangTypeImpl () +{ + return ClangASTType(); +} + +ConstString +ValueObjectRegisterSet::GetTypeName() +{ + return ConstString(); +} + +ConstString +ValueObjectRegisterSet::GetQualifiedTypeName() +{ + return ConstString(); +} + +size_t +ValueObjectRegisterSet::CalculateNumChildren() +{ + const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx); + if (reg_set) + return reg_set->num_registers; + 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 == NULL) + 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 == NULL) + 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 = NULL; + 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 (const ConstString &name, bool can_create) +{ + ValueObject *valobj = NULL; + if (m_reg_ctx_sp && m_reg_set) + { + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoByName (name.AsCString()); + if (reg_info != NULL) + valobj = new ValueObjectRegister(*this, m_reg_ctx_sp, reg_info->kinds[eRegisterKindLLDB]); + } + if (valobj) + return valobj->GetSP(); + else + return ValueObjectSP(); +} + +size_t +ValueObjectRegisterSet::GetIndexOfChildWithName (const ConstString &name) +{ + if (m_reg_ctx_sp && m_reg_set) + { + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoByName (name.AsCString()); + if (reg_info != NULL) + 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_clang_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_clang_type () +{ + assert (reg_ctx); + ConstructObject(reg_num); +} + +ValueObjectRegister::~ValueObjectRegister() +{ +} + +ClangASTType +ValueObjectRegister::GetClangTypeImpl () +{ + if (!m_clang_type.IsValid()) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + Module *exe_module = target->GetExecutableModulePointer(); + if (exe_module) + { + m_clang_type = exe_module->GetClangASTContext().GetBuiltinTypeForEncodingAndBitSize (m_reg_info.encoding, + m_reg_info.byte_size * 8); + } + } + } + return m_clang_type; +} + +ConstString +ValueObjectRegister::GetTypeName() +{ + if (m_type_name.IsEmpty()) + m_type_name = GetClangType().GetConstTypeName (); + return m_type_name; +} + +size_t +ValueObjectRegister::CalculateNumChildren() +{ + return GetClangType().GetNumChildren(true); +} + +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 == NULL) + { + m_reg_ctx_sp.reset(); + m_reg_value.Clear(); + } + + + if (m_reg_ctx_sp) + { + 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); + return true; + } + } + } + + SetValueIsValid (false); + m_error.SetErrorToGenericError (); + return false; +} + +bool +ValueObjectRegister::SetValueFromCString (const char *value_str, Error& error) +{ + // The new value will be in the m_data. Copy that into our register value. + error = m_reg_value.SetValueFromCString (&m_reg_info, 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, Error &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/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp new file mode 100644 index 0000000..522ca08 --- /dev/null +++ b/source/Core/ValueObjectSyntheticFilter.cpp @@ -0,0 +1,270 @@ +//===-- ValueObjectSyntheticFilter.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormatClasses.h" + +using namespace lldb_private; + +class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd +{ +public: + DummySyntheticFrontEnd(ValueObject &backend) : + SyntheticChildrenFrontEnd(backend) + {} + + size_t + CalculateNumChildren() + { + return 0; + } + + lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + return lldb::ValueObjectSP(); + } + + size_t + GetIndexOfChildWithName (const ConstString &name) + { + return UINT32_MAX; + } + + bool + MightHaveChildren () + { + return true; + } + + bool + Update() + { + 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_parent_type_name(parent.GetTypeName()), + m_might_have_children(eLazyBoolCalculate) +{ +#ifdef LLDB_CONFIGURATION_DEBUG + std::string new_name(parent.GetName().AsCString()); + new_name += "$$__synth__"; + SetName (ConstString(new_name.c_str())); +#else + SetName(parent.GetName()); +#endif + CopyParentData(); + CreateSynthFilter(); +} + +ValueObjectSynthetic::~ValueObjectSynthetic() +{ +} + +ClangASTType +ValueObjectSynthetic::GetClangTypeImpl () +{ + return m_parent->GetClangType(); +} + +ConstString +ValueObjectSynthetic::GetTypeName() +{ + return m_parent->GetTypeName(); +} + +ConstString +ValueObjectSynthetic::GetQualifiedTypeName() +{ + return m_parent->GetQualifiedTypeName(); +} + +size_t +ValueObjectSynthetic::CalculateNumChildren() +{ + UpdateValueIfNeeded(); + if (m_synthetic_children_count < UINT32_MAX) + return m_synthetic_children_count; + return (m_synthetic_children_count = m_synth_filter_ap->CalculateNumChildren()); +} + +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_ap->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo); + return (m_might_have_children == eLazyBoolNo ? false : true); +} + +uint64_t +ValueObjectSynthetic::GetByteSize() +{ + return m_parent->GetByteSize(); +} + +lldb::ValueType +ValueObjectSynthetic::GetValueType() const +{ + return m_parent->GetValueType(); +} + +void +ValueObjectSynthetic::CreateSynthFilter () +{ + m_synth_filter_ap = (m_synth_sp->GetFrontEnd(*m_parent)); + if (!m_synth_filter_ap.get()) + m_synth_filter_ap.reset(new DummySyntheticFrontEnd(*m_parent)); +} + +bool +ValueObjectSynthetic::UpdateValue () +{ + 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) + { + m_parent_type_name = new_parent_type_name; + CreateSynthFilter(); + } + + // let our backend do its update + if (m_synth_filter_ap->Update() == false) + { + // 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_count = UINT32_MAX; + m_might_have_children = eLazyBoolCalculate; + } + + CopyParentData(); + + SetValueIsValid(true); + return true; +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetChildAtIndex (size_t idx, bool can_create) +{ + UpdateValueIfNeeded(); + + ByIndexIterator iter = m_children_byindex.find(idx); + + if (iter == m_children_byindex.end()) + { + if (can_create && m_synth_filter_ap.get() != NULL) + { + lldb::ValueObjectSP synth_guy = m_synth_filter_ap->GetChildAtIndex (idx); + if (!synth_guy) + return synth_guy; + m_children_byindex[idx]= synth_guy.get(); + return synth_guy; + } + else + return lldb::ValueObjectSP(); + } + else + return iter->second->GetSP(); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetChildMemberWithName (const 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 (const ConstString &name) +{ + UpdateValueIfNeeded(); + + NameToIndexIterator iter = m_name_toindex.find(name.GetCString()); + + if (iter == m_name_toindex.end() && m_synth_filter_ap.get() != NULL) + { + uint32_t index = m_synth_filter_ap->GetIndexOfChildWithName (name); + if (index == UINT32_MAX) + return index; + m_name_toindex[name.GetCString()] = index; + return index; + } + else if (iter == m_name_toindex.end() && m_synth_filter_ap.get() == NULL) + return UINT32_MAX; + else /*if (iter != m_name_toindex.end())*/ + return iter->second; +} + +bool +ValueObjectSynthetic::IsInScope () +{ + return m_parent->IsInScope(); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetNonSyntheticValue () +{ + return m_parent->GetSP(); +} + +void +ValueObjectSynthetic::CopyParentData () +{ + m_value = m_parent->GetValue(); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); +} diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp new file mode 100644 index 0000000..38c0d91 --- /dev/null +++ b/source/Core/ValueObjectVariable.cpp @@ -0,0 +1,386 @@ +//===-- ValueObjectVariable.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.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/Target/Thread.h" + + +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() != NULL); + m_name = var_sp->GetName(); +} + +ValueObjectVariable::~ValueObjectVariable() +{ +} + +ClangASTType +ValueObjectVariable::GetClangTypeImpl () +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetClangForwardType(); + return ClangASTType(); +} + +ConstString +ValueObjectVariable::GetTypeName() +{ + Type * var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetName(); + 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() +{ + ClangASTType type(GetClangType()); + + if (!type.IsValid()) + return 0; + + const bool omit_empty_base_classes = true; + return type.GetNumChildren(omit_empty_base_classes); +} + +uint64_t +ValueObjectVariable::GetByteSize() +{ + ClangASTType type(GetClangType()); + + if (!type.IsValid()) + return 0; + + return type.GetByteSize(); +} + +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, NULL); + } + 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, NULL, NULL, NULL, loclist_base_load_addr, NULL, m_value, &m_error)) + { + m_resolved_value = m_value; + m_value.SetContext(Value::eContextTypeVariable, variable); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeFileAddress: + SetAddressTypeOfChildren(eAddressTypeFile); + break; + case Value::eValueTypeHostAddress: + 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, 0, 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. + Process *process = exe_ctx.GetProcessPtr(); + if (value_type == Value::eValueTypeFileAddress && process && process->IsAlive()) + { + lldb::addr_t file_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (file_addr != LLDB_INVALID_ADDRESS) + { + SymbolContext var_sc; + variable->CalculateSymbolContext(&var_sc); + if (var_sc.module_sp) + { + ObjectFile *objfile = var_sc.module_sp->GetObjectFile(); + if (objfile) + { + Address so_addr(file_addr, objfile->GetSectionList()); + lldb::addr_t load_addr = so_addr.GetLoadAddress (target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeLoadAddress); + m_value.GetScalar() = load_addr; + } + } + } + } + } + + if (GetClangType().IsAggregateType()) + { + // 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, 0, GetModule().get()); + } + break; + } + + SetValueIsValid (m_error.Success()); + } + else + { + // could not find location, won't allow editing + m_resolved_value.SetContext(Value::eContextTypeInvalid, NULL); + } + } + 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 NULL; +} + +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, Error& error) +{ + 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.SetValueFromCString(reg_info, 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, Error &error) +{ + 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, false); + 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); +} |