diff options
Diffstat (limited to 'lldb/source/Core')
55 files changed, 23710 insertions, 0 deletions
diff --git a/lldb/source/Core/Address.cpp b/lldb/source/Core/Address.cpp new file mode 100644 index 00000000000..e2481e133fa --- /dev/null +++ b/lldb/source/Core/Address.cpp @@ -0,0 +1,875 @@ +//===-- 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/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.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; + + lldb::AddressType addr_type = eAddressTypeInvalid; + addr_t addr = LLDB_INVALID_ADDRESS; + + Process *process = exe_scope->CalculateProcess(); + + if (process && process->IsAlive()) + { + addr = address.GetLoadAddress(process); + if (addr != LLDB_INVALID_ADDRESS) + addr_type = eAddressTypeLoad; + } + + if (addr == LLDB_INVALID_ADDRESS) + { + addr = address.GetFileAddress(); + if (addr != LLDB_INVALID_ADDRESS) + addr_type = eAddressTypeFile; + } + + if (addr_type == eAddressTypeInvalid) + return false; + + Target *target = exe_scope->CalculateTarget(); + if (target) + { + Error error; + return target->ReadMemory (addr_type, addr, dst, dst_len, error, NULL); + } + 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; + + Process *process = exe_scope->CalculateProcess(); + if (process) + { + byte_order = process->GetByteOrder(); + addr_size = process->GetAddressByteSize(); + } + + if (byte_order == eByteOrderInvalid || addr_size == 0) + { + Module *module = address.GetModule(); + if (module) + { + byte_order = module->GetArchitecture().GetDefaultEndian(); + addr_size = module->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; + + 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); + uint32_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) + { + Process *process = exe_scope->CalculateProcess(); + if (process && process->IsAlive()) + { + if (!process->ResolveLoadAddress (deref_addr, deref_so_addr)) + { + deref_so_addr.SetSection(NULL); + deref_so_addr.SetOffset(deref_addr); + } + } + else + { + Target *target = exe_scope->CalculateTarget(); + if (target == NULL) + return false; + + if (!target->GetImages().ResolveFileAddress(deref_addr, deref_so_addr)) + { + deref_so_addr.SetSection(NULL); + deref_so_addr.SetOffset(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) + 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.data(), 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 adderss size don't matter for C string dumping.. + DataExtractor data (buf, sizeof(buf), eByteOrderHost, 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 () : + m_section (NULL), + m_offset (LLDB_INVALID_ADDRESS) +{ +} + +Address::Address (const Address& rhs) : + m_section (rhs.m_section), + m_offset (rhs.m_offset) +{ +} + +Address::Address (const Section* section, addr_t offset) : + m_section (section), + m_offset (offset) +{ +} + +Address::Address (addr_t address, const SectionList * sections) : + m_section (NULL), + m_offset (LLDB_INVALID_ADDRESS) +{ + ResolveAddressUsingFileSections(address, sections); +} + +Address::~Address () +{ +} + + +const Address& +Address::operator= (const Address& rhs) +{ + if (this != &rhs) + { + m_section = rhs.m_section; + m_offset = rhs.m_offset; + } + return *this; +} + +bool +Address::IsValid() const +{ + return m_offset != LLDB_INVALID_ADDRESS; +} + +bool +Address::IsSectionOffset() const +{ + return m_section != NULL && IsValid(); +} + +bool +Address::ResolveAddressUsingFileSections (addr_t addr, const SectionList *sections) +{ + if (sections) + m_section = sections->FindSectionContainingFileAddress(addr).get(); + else + m_section = NULL; + + if (m_section != NULL) + { + assert( m_section->ContainsFileAddress(addr) ); + m_offset = addr - m_section->GetFileAddress(); + return true; // Successfully transformed addr into a section offset address + } + + m_offset = addr; + return false; // Failed to resolve this address to a section offset value +} + +//bool +//Address::ResolveAddressUsingLoadSections (addr_t addr, const SectionList *sections) +//{ +// if (sections) +// m_section = sections->FindSectionContainingLoadAddress(addr).get(); +// else +// m_section = NULL; +// +// if (m_section != NULL) +// { +// assert( m_section->ContainsLoadAddress(addr) ); +// m_offset = addr - m_section->GetLoadBaseAddress(); +// return true; // Successfully transformed addr into a section offset address +// } +// +// m_offset = addr; +// return false; // Failed to resolve this address to a section offset value +//} +// +Module * +Address::GetModule () const +{ + if (m_section) + return m_section->GetModule(); + return NULL; +} + +const Section* +Address::GetSection () const +{ + return m_section; +} + + +//addr_t +//Address::Address() const +//{ +// addr_t addr = GetLoadAddress(); +// if (addr != LLDB_INVALID_ADDRESS) +// return addr; +// return GetFileAddress(); +//} +// + +addr_t +Address::GetFileAddress () const +{ + if (m_section != NULL) + { + addr_t sect_file_addr = m_section->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 (Process *process) const +{ + if (m_section != NULL) + { + if (process) + { + addr_t sect_load_addr = m_section->GetLoadBaseAddress (process); + + 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; + } + // No section, we just return the offset since it is the value in this case + return m_offset; +} + +addr_t +Address::GetOffset () const +{ + return m_offset; +} + +bool +Address::SetOffset (addr_t offset) +{ + bool changed = m_offset != offset; + m_offset = offset; + return changed; +} + +void +Address::SetSection (const Section* section) +{ + m_section = section; +} + +void +Address::Clear() +{ + m_section = NULL; + m_offset = LLDB_INVALID_ADDRESS; +} + + +bool +Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style) const +{ + // If the section was NULL, only load address is going to work. + if (m_section == NULL) + style = DumpStyleLoadAddress; + + Process *process = NULL; + if (exe_scope) + process = exe_scope->CalculateProcess(); + int addr_size = sizeof (addr_t); + if (process) + addr_size = process->GetAddressByteSize (); + + lldb_private::Address so_addr; + + switch (style) + { + case DumpStyleSectionNameOffset: + if (m_section != NULL) + { + m_section->DumpName(s); + s->Printf (" + %llu", m_offset); + } + else + { + s->Printf("0x%16.16llx", m_offset); + } + break; + + case DumpStyleSectionPointerOffset: + s->Printf("(Section *)%.*p + 0x%16.16llx", (int)sizeof(void*) * 2, m_section, m_offset); + break; + + case DumpStyleModuleWithFileAddress: + s->Printf("%s[", m_section->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); + return false; + } + s->Address (file_addr, addr_size); + if (style == DumpStyleModuleWithFileAddress) + s->PutChar(']'); + } + break; + + case DumpStyleLoadAddress: + { + addr_t load_addr = GetLoadAddress (process); + if (load_addr == LLDB_INVALID_ADDRESS) + { + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style); + return false; + } + s->Address (load_addr, addr_size); + } + break; + + case DumpStyleResolvedDescription: + if (IsSectionOffset()) + { + lldb::AddressType addr_type = eAddressTypeLoad; + addr_t addr = GetLoadAddress (process); + if (addr == LLDB_INVALID_ADDRESS) + { + addr = GetFileAddress(); + addr_type = eAddressTypeFile; + } + + uint32_t pointer_size = 4; + lldb_private::Module *module = GetModule(); + if (process) + pointer_size = process->GetAddressByteSize(); + else if (module) + pointer_size = module->GetArchitecture().GetAddressByteSize(); + + bool showed_info = false; + const Section *section = GetSection(); + if (section) + { + SectionType sect_type = section->GetSectionType(); + switch (sect_type) + { + 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 (so_addr.IsSectionOffset()) + { + lldb_private::SymbolContext func_sc; + process->GetTarget().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, process, so_addr, true); + if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("), (char *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(" ("); +#else + s->PutCString(", "); +#endif + ReadCStringFromMemory (exe_scope, so_addr, s); + } +#if VERBOSE_OUTPUT + s->PutCString(") }"); +#else + s->PutCString(" }"); +#endif + } + } + } + } + break; + + case eSectionTypeDataObjCCFStrings: + { + Address cfstring_data_addr(*this); + cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() + (2 * pointer_size)); + if (ReadAddress (exe_scope, cfstring_data_addr, pointer_size, so_addr)) + { +#if VERBOSE_OUTPUT + s->PutCString("(CFString *) "); + cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + s->PutCString(" -> @"); +#else + s->PutChar('@'); +#endif + if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription)) + showed_info = true; + } + } + break; + + case eSectionTypeData4: + // Read the 4 byte data and display it + showed_info = true; + s->PutCString("(uint32_t) "); + DumpUInt (exe_scope, *this, 4, s); + break; + + case eSectionTypeData8: + // Read the 8 byte data and display it + showed_info = true; + s->PutCString("(uint64_t) "); + DumpUInt (exe_scope, *this, 8, s); + break; + + case eSectionTypeData16: + // Read the 16 byte data and display it + showed_info = true; + s->PutCString("(uint128_t) "); + DumpUInt (exe_scope, *this, 16, s); + break; + + case eSectionTypeDataPointers: + // Read the pointer data and display it + { + if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) + { + s->PutCString ("(void *)"); + so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); + + showed_info = true; + if (so_addr.IsSectionOffset()) + { + lldb_private::SymbolContext pointer_sc; + process->GetTarget().GetImages().ResolveSymbolContextForAddress (so_addr, + eSymbolContextEverything, + pointer_sc); + if (pointer_sc.function || pointer_sc.symbol) + { + s->PutCString(": "); + pointer_sc.DumpStopContext(s, process, so_addr, false); + } + } + } + } + break; + } + } + + if (!showed_info) + { + if (module) + { + lldb_private::SymbolContext sc; + module->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); + if (sc.function || sc.symbol) + { + bool show_stop_context = 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->GetAddressRangePtr()) + { + if (sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetSection() != GetSection()) + 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, process, *this, false); + } + 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); + return false; + } + break; + } + + return true; +} + +//Stream& operator << (Stream& s, const Address& so_addr) +//{ +// so_addr.Dump(&s, Address::DumpStyleSectionNameOffset); +// return s; +//} +// +void +Address::CalculateSymbolContext (SymbolContext *sc) +{ + sc->Clear(); + // Absolute addresses don't have enough information to reconstruct even their target. + if (m_section == NULL) + return; + + if (m_section->GetModule()) + { + sc->module_sp = m_section->GetModule()->GetSP(); + if (sc->module_sp) + sc->module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextEverything, *sc); + } +} + +void +Address::DumpSymbolContext (Stream *s) +{ + SymbolContext sc; + CalculateSymbolContext (&sc); + sc.Dump (s, NULL); +} + +void +Address::DumpDebug(Stream *s) const +{ + *s << (void *)this << ": " << "Address"; + if (m_section != NULL) + { + *s << ", section = " << (void *)m_section << " (" << m_section->GetName() << "), offset = " << m_offset; + } + else + { + *s << ", vm_addr = " << m_offset; + } + s->EOL(); +} + +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, Process *process) +{ + assert (process != NULL); + addr_t a_load_addr = a.GetLoadAddress (process); + addr_t b_load_addr = b.GetLoadAddress (process); + 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) +{ + Module *a_module = a.GetModule (); + Module *b_module = b.GetModule (); + 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); +} + + +/// The only comparisons that make sense are the load addresses +//bool +//lldb::operator< (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr < rhs_addr; +//} +// +//bool +//lldb::operator<= (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr <= rhs_addr; +//} +// +//bool +//lldb::operator> (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr > rhs_addr; +//} +// +//bool +//lldb::operator>= (const Address& lhs, const Address& rhs) +//{ +// lldb::addr_t lhs_addr = lhs.GetLoadAddress(); +// lldb::addr_t rhs_addr = rhs.GetLoadAddress(); +// +// if (lhs_addr == rhs_addr) +// { +// lhs_addr = lhs.GetFileAddress(); +// rhs_addr = rhs.GetFileAddress(); +// } +// return lhs_addr >= rhs_addr; +//} +// + +// The operator == checks for exact equality only (same section, same offset) +bool +lldb_private::operator== (const Address& a, const Address& rhs) +{ + return a.GetSection() == rhs.GetSection() && + a.GetOffset() == rhs.GetOffset(); +} +// The operator != checks for exact inequality only (differing section, or +// different offset) +bool +lldb_private::operator!= (const Address& a, const Address& rhs) +{ + return a.GetSection() != rhs.GetSection() || + a.GetOffset() != rhs.GetOffset(); +} + +bool +Address::IsLinkedAddress () const +{ + return m_section && m_section->GetLinkedSection(); +} + + +void +Address::ResolveLinkedAddress () +{ + if (m_section) + { + const Section *linked_section = m_section->GetLinkedSection(); + if (linked_section) + { + m_offset += m_section->GetLinkedOffset(); + m_section = linked_section; + } + } +} diff --git a/lldb/source/Core/AddressRange.cpp b/lldb/source/Core/AddressRange.cpp new file mode 100644 index 00000000000..8c2997936fd --- /dev/null +++ b/lldb/source/Core/AddressRange.cpp @@ -0,0 +1,222 @@ +//===-- 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/Stream.h" +#include "lldb/Target/Process.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 Section* section, addr_t offset, addr_t byte_size) : + m_base_addr(section, offset), + m_byte_size(byte_size) +{ +} + +AddressRange::AddressRange (const Address& so_addr, addr_t byte_size) : + m_base_addr(so_addr), + m_byte_size(byte_size) +{ +} + +AddressRange::~AddressRange () +{ +} + +Address & +AddressRange::GetBaseAddress() +{ + return m_base_addr; +} + +const Address & +AddressRange::GetBaseAddress() const +{ + return m_base_addr; +} + +addr_t +AddressRange::GetByteSize() const +{ + return m_byte_size; +} + +void +AddressRange::SetByteSize(addr_t byte_size) +{ + m_byte_size = byte_size; +} + +//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, Process *process) const +{ + if (addr.GetSection() == m_base_addr.GetSection()) + return (addr.GetOffset() - m_base_addr.GetOffset()) < GetByteSize(); + addr_t load_base_addr = GetBaseAddress().GetLoadAddress(process); + if (load_base_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t load_addr = addr.GetLoadAddress(process); + 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, Process *process) const +{ + if (load_addr == LLDB_INVALID_ADDRESS) + return false; + + addr_t load_base_addr = GetBaseAddress().GetLoadAddress(process); + 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, Process *process, Address::DumpStyle style, Address::DumpStyle fallback_style) const +{ + addr_t vmaddr = LLDB_INVALID_ADDRESS; + int addr_size = sizeof (addr_t); + if (process) + addr_size = process->GetAddressByteSize (); + + switch (style) + { + case Address::DumpStyleSectionNameOffset: + case Address::DumpStyleSectionPointerOffset: + s->PutChar ('['); + m_base_addr.Dump(s, process, style, fallback_style); + s->PutChar ('-'); + s->Address (m_base_addr.GetOffset() + GetByteSize(), addr_size); + s->PutChar (')'); + return true; + break; + + case Address::DumpStyleFileAddress: + vmaddr = m_base_addr.GetFileAddress(); + break; + + case Address::DumpStyleLoadAddress: + vmaddr = m_base_addr.GetLoadAddress(process); + break; + } + + if (vmaddr != LLDB_INVALID_ADDRESS) + { + s->AddressRange(vmaddr, vmaddr + GetByteSize(), addr_size); + return true; + } + + return false; +} + + +void +AddressRange::DumpDebug (Stream *s) const +{ + s->Printf("%.*p: AddressRange section = %*p, offset = 0x%16.16llx, byte_size = 0x%16.16llx\n", (int)sizeof(void*) * 2, this, (int)sizeof(void*) * 2, m_base_addr.GetSection(), m_base_addr.GetOffset(), GetByteSize()); +} + +size_t +AddressRange::MemorySize () const +{ + // Noting special for the memory size of a single AddressRange object, + // it is just the size of itself. + return sizeof(AddressRange); +} +// +//bool +//lldb::operator== (const AddressRange& lhs, const AddressRange& rhs) +//{ +// if (lhs.GetBaseAddress() == rhs.GetBaseAddress()) +// return lhs.GetByteSize() == rhs.GetByteSize(); +// return false; +//} diff --git a/lldb/source/Core/AddressResolver.cpp b/lldb/source/Core/AddressResolver.cpp new file mode 100644 index 00000000000..5369d960f25 --- /dev/null +++ b/lldb/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/lldb/source/Core/AddressResolverFileLine.cpp b/lldb/source/Core/AddressResolverFileLine.cpp new file mode 100644 index 00000000000..c4aadcca9c1 --- /dev/null +++ b/lldb/source/Core/AddressResolverFileLine.cpp @@ -0,0 +1,100 @@ +//===-- 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/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 (int 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%llx 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/lldb/source/Core/AddressResolverName.cpp b/lldb/source/Core/AddressResolverName.cpp new file mode 100644 index 00000000000..9e154c6e703 --- /dev/null +++ b/lldb/source/Core/AddressResolverName.cpp @@ -0,0 +1,231 @@ +//===-- 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/StreamString.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; + } + + 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, false, 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, true, 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->GetAddressRangePtr()) + { + if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddressRangePtr()->GetBaseAddress()) + { + 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->GetAddressRangePtr()) + { + func_addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress(); + addr_t byte_size = sc.symbol->GetAddressRangePtr()->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/lldb/source/Core/ArchSpec.cpp b/lldb/source/Core/ArchSpec.cpp new file mode 100644 index 00000000000..3cc8232ac97 --- /dev/null +++ b/lldb/source/Core/ArchSpec.cpp @@ -0,0 +1,1681 @@ +//===-- 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 <mach/mach.h> +#include <mach-o/nlist.h> + +#include <string> + +using namespace lldb; +using namespace lldb_private; + +#define ARCH_SPEC_SEPARATOR_CHAR '-' + +#ifndef CPU_TYPE_ARM +#define CPU_TYPE_ARM ((cpu_type_t) 12) +#endif + +#ifndef CPU_SUBTYPE_ARM_ALL +#define CPU_SUBTYPE_ARM_ALL ((cpu_subtype_t) 0) +#endif + +#ifndef CPU_SUBTYPE_ARM_V4T +#define CPU_SUBTYPE_ARM_V4T ((cpu_subtype_t) 5) +#endif + +#ifndef CPU_SUBTYPE_ARM_V6 +#define CPU_SUBTYPE_ARM_V6 ((cpu_subtype_t) 6) +#endif + +#ifndef CPU_SUBTYPE_ARM_V5TEJ +#define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif + +#ifndef CPU_SUBTYPE_ARM_XSCALE +#define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7 +#define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif + +//---------------------------------------------------------------------- +// A structure that describes all of the information we want to know +// about each architecture. +//---------------------------------------------------------------------- +struct ArchDefinition +{ + uint32_t cpu; + uint32_t sub; + const char *name; +}; + +//---------------------------------------------------------------------- +// 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 ArchDefinition g_arch_defs[] = +{ + { CPU_TYPE_ANY, CPU_TYPE_ANY , "all" }, + { CPU_TYPE_ARM, CPU_TYPE_ANY , "arm" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_ALL , "arm" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T , "armv4" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ , "armv5" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 , "armv6" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 , "armv7" }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_XSCALE , "xscale" }, + { CPU_TYPE_POWERPC, CPU_TYPE_ANY , "ppc" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL , "ppc" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_601 , "ppc601" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_602 , "ppc602" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603 , "ppc603" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603e , "ppc603e" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_603ev , "ppc603ev" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604 , "ppc604" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_604e , "ppc604e" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_620 , "ppc620" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750 , "ppc750" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 , "ppc7400" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7450 , "ppc7450" }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_970 , "ppc970" }, + { CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL , "ppc64" }, + { CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_970 , "ppc970-64" }, + { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL , "i386" }, + { CPU_TYPE_I386, CPU_SUBTYPE_486 , "i486" }, + { CPU_TYPE_I386, CPU_SUBTYPE_486SX , "i486sx" }, + { CPU_TYPE_I386, CPU_TYPE_ANY , "i386" }, + { CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL , "x86_64" }, + { CPU_TYPE_X86_64, CPU_TYPE_ANY , "x86_64" }, + + // TODO: when we get a platform that knows more about the host OS we should + // let it call some accessor funcitons to set the default system arch for + // the default, 32 and 64 bit cases instead of hard coding it in this + // table. + +#if defined (__i386__) || defined(__x86_64__) + { CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL , LLDB_ARCH_DEFAULT }, + { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL , LLDB_ARCH_DEFAULT_32BIT }, + { CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL , LLDB_ARCH_DEFAULT_64BIT }, +#elif defined (__arm__) + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 , LLDB_ARCH_DEFAULT }, + { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 , LLDB_ARCH_DEFAULT_32BIT }, +#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 , LLDB_ARCH_DEFAULT }, + { CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400 , LLDB_ARCH_DEFAULT_32BIT }, + { CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_970 , LLDB_ARCH_DEFAULT_64BIT }, +#endif +}; + +//---------------------------------------------------------------------- +// Figure out how many architecture definitions we have +//---------------------------------------------------------------------- +const size_t k_num_arch_defs = sizeof(g_arch_defs)/sizeof(ArchDefinition); + + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +ArchSpec::ArchSpec() : + m_cpu (LLDB_INVALID_CPUTYPE), + m_sub (0) +{ +} + +//---------------------------------------------------------------------- +// Constructor that initializes the object with supplied cpu and +// subtypes. +//---------------------------------------------------------------------- +ArchSpec::ArchSpec(uint32_t cpu, uint32_t sub) : + m_cpu (cpu), + m_sub (sub) +{ +} + +//---------------------------------------------------------------------- +// Constructor that initializes the object with supplied +// architecture name. There are also predefined values in +// Defines.h: +// liblldb_ARCH_DEFAULT +// The arch the current system defaults to when a program is +// launched without any extra attributes or settings. +// +// liblldb_ARCH_DEFAULT_32BIT +// The 32 bit arch the current system defaults to (if any) +// +// liblldb_ARCH_DEFAULT_32BIT +// The 64 bit arch the current system defaults to (if any) +//---------------------------------------------------------------------- +ArchSpec::ArchSpec(const char *arch_name) : + m_cpu (LLDB_INVALID_CPUTYPE), + m_sub (0) +{ + if (arch_name) + SetArch(arch_name); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ArchSpec::~ArchSpec() +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const ArchSpec& +ArchSpec::operator= (const ArchSpec& rhs) +{ + if (this != &rhs) + { + m_cpu = rhs.m_cpu; + m_sub = rhs.m_sub; + } + return *this; +} + +//---------------------------------------------------------------------- +// Get a C string representation of the current architecture +//---------------------------------------------------------------------- +const char * +ArchSpec::AsCString() const +{ + return ArchSpec::AsCString(m_cpu, m_sub); +} + +//---------------------------------------------------------------------- +// Class function to get a C string representation given a CPU type +// and subtype. +//---------------------------------------------------------------------- +const char * +ArchSpec::AsCString(uint32_t cpu, uint32_t sub) +{ + for (uint32_t i=0; i<k_num_arch_defs; i++) + { + if (cpu == g_arch_defs[i].cpu) + { + if (sub == g_arch_defs[i].sub) + return g_arch_defs[i].name; + else if (sub != CPU_TYPE_ANY && sub != LLDB_INVALID_CPUTYPE) + { + if ((sub & ~CPU_SUBTYPE_MASK) == g_arch_defs[i].sub) + return g_arch_defs[i].name; + } + } + } + static char s_cpu_hex_str[64]; + ::snprintf(s_cpu_hex_str, sizeof(s_cpu_hex_str), "%u%c%u", cpu, ARCH_SPEC_SEPARATOR_CHAR, sub); + return s_cpu_hex_str; +} + +//---------------------------------------------------------------------- +// Clears the object contents back to a default invalid state. +//---------------------------------------------------------------------- +void +ArchSpec::Clear() +{ + m_cpu = LLDB_INVALID_CPUTYPE; + m_sub = 0; +} + + +//---------------------------------------------------------------------- +// CPU subtype get accessor. +//---------------------------------------------------------------------- +uint32_t +ArchSpec::GetCPUSubtype() const +{ + if (m_sub == CPU_TYPE_ANY || m_sub == LLDB_INVALID_CPUTYPE) + return m_sub; + return m_sub & ~CPU_SUBTYPE_MASK; +} + + +//---------------------------------------------------------------------- +// CPU type get accessor. +//---------------------------------------------------------------------- +uint32_t +ArchSpec::GetCPUType() const +{ + return m_cpu; +} + + +//---------------------------------------------------------------------- +// Feature flags get accessor. +//---------------------------------------------------------------------- +uint32_t +ArchSpec::GetFeatureFlags() const +{ + if (m_sub == CPU_TYPE_ANY || m_sub == LLDB_INVALID_CPUTYPE) + return 0; + return m_sub & CPU_SUBTYPE_MASK; +} + + +static const char * g_i386_dwarf_reg_names[] = +{ + "eax", + "ecx", + "edx", + "ebx", + "esp", + "ebp", + "esi", + "edi", + "eip", + "eflags" +}; + +static const char * g_i386_gcc_reg_names[] = +{ + "eax", + "ecx", + "edx", + "ebx", + "ebp", + "esp", + "esi", + "edi", + "eip", + "eflags" +}; + +static const char * g_x86_64_dwarf_and_gcc_reg_names[] = { + "rax", + "rdx", + "rcx", + "rbx", + "rsi", + "rdi", + "rbp", + "rsp", + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "rip" +}; + +// Values take from: +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf + +enum +{ + eRegNumARM_DWARF_r0 = 0, + eRegNumARM_DWARF_r1 = 1, + eRegNumARM_DWARF_r2 = 2, + eRegNumARM_DWARF_r3 = 3, + eRegNumARM_DWARF_r4 = 4, + eRegNumARM_DWARF_r5 = 5, + eRegNumARM_DWARF_r6 = 6, + eRegNumARM_DWARF_r7 = 7, + eRegNumARM_DWARF_r8 = 8, + eRegNumARM_DWARF_r9 = 9, + eRegNumARM_DWARF_r10 = 10, + eRegNumARM_DWARF_r11 = 11, + eRegNumARM_DWARF_r12 = 12, + eRegNumARM_DWARF_r13 = 13, // SP + eRegNumARM_DWARF_r14 = 14, // LR + eRegNumARM_DWARF_r15 = 15, // PC + + eRegNumARM_DWARF_f0_obsolete= 16, + eRegNumARM_DWARF_f1_obsolete, + eRegNumARM_DWARF_f2_obsolete, + eRegNumARM_DWARF_f3_obsolete, + eRegNumARM_DWARF_f4_obsolete, + eRegNumARM_DWARF_f5_obsolete, + eRegNumARM_DWARF_f6_obsolete, + eRegNumARM_DWARF_f7_obsolete, + + eRegNumARM_DWARF_s0_obsolete = 16, + eRegNumARM_DWARF_s1_obsolete, + eRegNumARM_DWARF_s2_obsolete, + eRegNumARM_DWARF_s3_obsolete, + eRegNumARM_DWARF_s4_obsolete, + eRegNumARM_DWARF_s5_obsolete, + eRegNumARM_DWARF_s6_obsolete, + eRegNumARM_DWARF_s7_obsolete, + eRegNumARM_DWARF_s8_obsolete, + eRegNumARM_DWARF_s9_obsolete, + eRegNumARM_DWARF_s10_obsolete, + eRegNumARM_DWARF_s11_obsolete, + eRegNumARM_DWARF_s12_obsolete, + eRegNumARM_DWARF_s13_obsolete, + eRegNumARM_DWARF_s14_obsolete, + eRegNumARM_DWARF_s15_obsolete, + eRegNumARM_DWARF_s16_obsolete, + eRegNumARM_DWARF_s17_obsolete, + eRegNumARM_DWARF_s18_obsolete, + eRegNumARM_DWARF_s19_obsolete, + eRegNumARM_DWARF_s20_obsolete, + eRegNumARM_DWARF_s21_obsolete, + eRegNumARM_DWARF_s22_obsolete, + eRegNumARM_DWARF_s23_obsolete, + eRegNumARM_DWARF_s24_obsolete, + eRegNumARM_DWARF_s25_obsolete, + eRegNumARM_DWARF_s26_obsolete, + eRegNumARM_DWARF_s27_obsolete, + eRegNumARM_DWARF_s28_obsolete, + eRegNumARM_DWARF_s29_obsolete, + eRegNumARM_DWARF_s30_obsolete, + eRegNumARM_DWARF_s31_obsolete, + + eRegNumARM_DWARF_s0 = 64, + eRegNumARM_DWARF_s1, + eRegNumARM_DWARF_s2, + eRegNumARM_DWARF_s3, + eRegNumARM_DWARF_s4, + eRegNumARM_DWARF_s5, + eRegNumARM_DWARF_s6, + eRegNumARM_DWARF_s7, + eRegNumARM_DWARF_s8, + eRegNumARM_DWARF_s9, + eRegNumARM_DWARF_s10, + eRegNumARM_DWARF_s11, + eRegNumARM_DWARF_s12, + eRegNumARM_DWARF_s13, + eRegNumARM_DWARF_s14, + eRegNumARM_DWARF_s15, + eRegNumARM_DWARF_s16, + eRegNumARM_DWARF_s17, + eRegNumARM_DWARF_s18, + eRegNumARM_DWARF_s19, + eRegNumARM_DWARF_s20, + eRegNumARM_DWARF_s21, + eRegNumARM_DWARF_s22, + eRegNumARM_DWARF_s23, + eRegNumARM_DWARF_s24, + eRegNumARM_DWARF_s25, + eRegNumARM_DWARF_s26, + eRegNumARM_DWARF_s27, + eRegNumARM_DWARF_s28, + eRegNumARM_DWARF_s29, + eRegNumARM_DWARF_s30, + eRegNumARM_DWARF_s31, + + eRegNumARM_DWARF_f0 = 96, + eRegNumARM_DWARF_f1, + eRegNumARM_DWARF_f2, + eRegNumARM_DWARF_f3, + eRegNumARM_DWARF_f4, + eRegNumARM_DWARF_f5, + eRegNumARM_DWARF_f6, + eRegNumARM_DWARF_f7, + + eRegNumARM_DWARF_ACC0 = 104, + eRegNumARM_DWARF_ACC1, + eRegNumARM_DWARF_ACC2, + eRegNumARM_DWARF_ACC3, + eRegNumARM_DWARF_ACC4, + eRegNumARM_DWARF_ACC5, + eRegNumARM_DWARF_ACC6, + eRegNumARM_DWARF_ACC7, + + eRegNumARM_DWARF_wCGR0 = 104, // These overlap with ACC0-ACC7 + eRegNumARM_DWARF_wCGR1, + eRegNumARM_DWARF_wCGR2, + eRegNumARM_DWARF_wCGR3, + eRegNumARM_DWARF_wCGR4, + eRegNumARM_DWARF_wCGR5, + eRegNumARM_DWARF_wCGR6, + eRegNumARM_DWARF_wCGR7, + + eRegNumARM_DWARF_wR0 = 112, + eRegNumARM_DWARF_wR1, + eRegNumARM_DWARF_wR2, + eRegNumARM_DWARF_wR3, + eRegNumARM_DWARF_wR4, + eRegNumARM_DWARF_wR5, + eRegNumARM_DWARF_wR6, + eRegNumARM_DWARF_wR7, + eRegNumARM_DWARF_wR8, + eRegNumARM_DWARF_wR9, + eRegNumARM_DWARF_wR10, + eRegNumARM_DWARF_wR11, + eRegNumARM_DWARF_wR12, + eRegNumARM_DWARF_wR13, + eRegNumARM_DWARF_wR14, + eRegNumARM_DWARF_wR15, + + eRegNumARM_DWARF_spsr = 128, + eRegNumARM_DWARF_spsr_fiq, + eRegNumARM_DWARF_spsr_irq, + eRegNumARM_DWARF_spsr_abt, + eRegNumARM_DWARF_spsr_und, + eRegNumARM_DWARF_spsr_svc, + + eRegNumARM_DWARF_r8_usr = 144, + eRegNumARM_DWARF_r9_usr, + eRegNumARM_DWARF_r10_usr, + eRegNumARM_DWARF_r11_usr, + eRegNumARM_DWARF_r12_usr, + eRegNumARM_DWARF_r13_usr, + eRegNumARM_DWARF_r14_usr, + + eRegNumARM_DWARF_r8_fiq = 151, + eRegNumARM_DWARF_r9_fiq, + eRegNumARM_DWARF_r10_fiq, + eRegNumARM_DWARF_r11_fiq, + eRegNumARM_DWARF_r12_fiq, + eRegNumARM_DWARF_r13_fiq, + eRegNumARM_DWARF_r14_fiq, + + eRegNumARM_DWARF_r13_irq, + eRegNumARM_DWARF_r14_irq, + + eRegNumARM_DWARF_r13_abt, + eRegNumARM_DWARF_r14_abt, + + eRegNumARM_DWARF_r13_und, + eRegNumARM_DWARF_r14_und, + + eRegNumARM_DWARF_r13_svc, + eRegNumARM_DWARF_r14_svc, + + eRegNumARM_DWARF_wC0 = 192, + eRegNumARM_DWARF_wC1, + eRegNumARM_DWARF_wC2, + eRegNumARM_DWARF_wC3, + eRegNumARM_DWARF_wC4, + eRegNumARM_DWARF_wC5, + eRegNumARM_DWARF_wC6, + eRegNumARM_DWARF_wC7, + + eRegNumARM_DWARF_d0 = 256, // VFP-v3/NEON D0-D31 (32 64 bit registers) + eRegNumARM_DWARF_d1, + eRegNumARM_DWARF_d2, + eRegNumARM_DWARF_d3, + eRegNumARM_DWARF_d4, + eRegNumARM_DWARF_d5, + eRegNumARM_DWARF_d6, + eRegNumARM_DWARF_d7, + eRegNumARM_DWARF_d8, + eRegNumARM_DWARF_d9, + eRegNumARM_DWARF_d10, + eRegNumARM_DWARF_d11, + eRegNumARM_DWARF_d12, + eRegNumARM_DWARF_d13, + eRegNumARM_DWARF_d14, + eRegNumARM_DWARF_d15, + eRegNumARM_DWARF_d16, + eRegNumARM_DWARF_d17, + eRegNumARM_DWARF_d18, + eRegNumARM_DWARF_d19, + eRegNumARM_DWARF_d20, + eRegNumARM_DWARF_d21, + eRegNumARM_DWARF_d22, + eRegNumARM_DWARF_d23, + eRegNumARM_DWARF_d24, + eRegNumARM_DWARF_d25, + eRegNumARM_DWARF_d26, + eRegNumARM_DWARF_d27, + eRegNumARM_DWARF_d28, + eRegNumARM_DWARF_d29, + eRegNumARM_DWARF_d30, + eRegNumARM_DWARF_d31 +}; + +// Register numbering definitions for 32 and 64 bit ppc for RegisterNumberingType::Dwarf +enum +{ + eRegNumPPC_DWARF_r0 = 0, + eRegNumPPC_DWARF_r1 = 1, + eRegNumPPC_DWARF_r2 = 2, + eRegNumPPC_DWARF_r3 = 3, + eRegNumPPC_DWARF_r4 = 4, + eRegNumPPC_DWARF_r5 = 5, + eRegNumPPC_DWARF_r6 = 6, + eRegNumPPC_DWARF_r7 = 7, + eRegNumPPC_DWARF_r8 = 8, + eRegNumPPC_DWARF_r9 = 9, + eRegNumPPC_DWARF_r10 = 10, + eRegNumPPC_DWARF_r11 = 11, + eRegNumPPC_DWARF_r12 = 12, + eRegNumPPC_DWARF_r13 = 13, + eRegNumPPC_DWARF_r14 = 14, + eRegNumPPC_DWARF_r15 = 15, + eRegNumPPC_DWARF_r16 = 16, + eRegNumPPC_DWARF_r17 = 17, + eRegNumPPC_DWARF_r18 = 18, + eRegNumPPC_DWARF_r19 = 19, + eRegNumPPC_DWARF_r20 = 20, + eRegNumPPC_DWARF_r21 = 21, + eRegNumPPC_DWARF_r22 = 22, + eRegNumPPC_DWARF_r23 = 23, + eRegNumPPC_DWARF_r24 = 24, + eRegNumPPC_DWARF_r25 = 25, + eRegNumPPC_DWARF_r26 = 26, + eRegNumPPC_DWARF_r27 = 27, + eRegNumPPC_DWARF_r28 = 28, + eRegNumPPC_DWARF_r29 = 29, + eRegNumPPC_DWARF_r30 = 30, + eRegNumPPC_DWARF_r31 = 31, + + eRegNumPPC_DWARF_fr0 = 32, + eRegNumPPC_DWARF_fr1 = 33, + eRegNumPPC_DWARF_fr2 = 34, + eRegNumPPC_DWARF_fr3 = 35, + eRegNumPPC_DWARF_fr4 = 36, + eRegNumPPC_DWARF_fr5 = 37, + eRegNumPPC_DWARF_fr6 = 38, + eRegNumPPC_DWARF_fr7 = 39, + eRegNumPPC_DWARF_fr8 = 40, + eRegNumPPC_DWARF_fr9 = 41, + eRegNumPPC_DWARF_fr10 = 42, + eRegNumPPC_DWARF_fr11 = 43, + eRegNumPPC_DWARF_fr12 = 44, + eRegNumPPC_DWARF_fr13 = 45, + eRegNumPPC_DWARF_fr14 = 46, + eRegNumPPC_DWARF_fr15 = 47, + eRegNumPPC_DWARF_fr16 = 48, + eRegNumPPC_DWARF_fr17 = 49, + eRegNumPPC_DWARF_fr18 = 50, + eRegNumPPC_DWARF_fr19 = 51, + eRegNumPPC_DWARF_fr20 = 52, + eRegNumPPC_DWARF_fr21 = 53, + eRegNumPPC_DWARF_fr22 = 54, + eRegNumPPC_DWARF_fr23 = 55, + eRegNumPPC_DWARF_fr24 = 56, + eRegNumPPC_DWARF_fr25 = 57, + eRegNumPPC_DWARF_fr26 = 58, + eRegNumPPC_DWARF_fr27 = 59, + eRegNumPPC_DWARF_fr28 = 60, + eRegNumPPC_DWARF_fr29 = 61, + eRegNumPPC_DWARF_fr30 = 62, + eRegNumPPC_DWARF_fr31 = 63, + + eRegNumPPC_DWARF_cr = 64, + eRegNumPPC_DWARF_fpscr = 65, + eRegNumPPC_DWARF_msr = 66, + eRegNumPPC_DWARF_vscr = 67, + + eRegNumPPC_DWARF_sr0 = 70, + eRegNumPPC_DWARF_sr1, + eRegNumPPC_DWARF_sr2, + eRegNumPPC_DWARF_sr3, + eRegNumPPC_DWARF_sr4, + eRegNumPPC_DWARF_sr5, + eRegNumPPC_DWARF_sr6, + eRegNumPPC_DWARF_sr7, + eRegNumPPC_DWARF_sr8, + eRegNumPPC_DWARF_sr9, + eRegNumPPC_DWARF_sr10, + eRegNumPPC_DWARF_sr11, + eRegNumPPC_DWARF_sr12, + eRegNumPPC_DWARF_sr13, + eRegNumPPC_DWARF_sr14, + eRegNumPPC_DWARF_sr15, + + + eRegNumPPC_DWARF_acc = 99, + eRegNumPPC_DWARF_mq = 100, + eRegNumPPC_DWARF_xer = 101, + eRegNumPPC_DWARF_rtcu = 104, + eRegNumPPC_DWARF_rtcl = 105, + + eRegNumPPC_DWARF_lr = 108, + eRegNumPPC_DWARF_ctr = 109, + + eRegNumPPC_DWARF_dsisr = 118, + eRegNumPPC_DWARF_dar = 119, + eRegNumPPC_DWARF_dec = 122, + eRegNumPPC_DWARF_sdr1 = 125, + eRegNumPPC_DWARF_srr0 = 126, + eRegNumPPC_DWARF_srr1 = 127, + + eRegNumPPC_DWARF_vrsave = 356, + eRegNumPPC_DWARF_sprg0 = 372, + eRegNumPPC_DWARF_sprg1, + eRegNumPPC_DWARF_sprg2, + eRegNumPPC_DWARF_sprg3, + + eRegNumPPC_DWARF_asr = 380, + eRegNumPPC_DWARF_ear = 382, + eRegNumPPC_DWARF_tb = 384, + eRegNumPPC_DWARF_tbu = 385, + eRegNumPPC_DWARF_pvr = 387, + + eRegNumPPC_DWARF_spefscr = 612, + + eRegNumPPC_DWARF_ibat0u = 628, + eRegNumPPC_DWARF_ibat0l = 629, + eRegNumPPC_DWARF_ibat1u = 630, + eRegNumPPC_DWARF_ibat1l = 631, + eRegNumPPC_DWARF_ibat2u = 632, + eRegNumPPC_DWARF_ibat2l = 633, + eRegNumPPC_DWARF_ibat3u = 634, + eRegNumPPC_DWARF_ibat3l = 635, + eRegNumPPC_DWARF_dbat0u = 636, + eRegNumPPC_DWARF_dbat0l = 637, + eRegNumPPC_DWARF_dbat1u = 638, + eRegNumPPC_DWARF_dbat1l = 639, + eRegNumPPC_DWARF_dbat2u = 640, + eRegNumPPC_DWARF_dbat2l = 641, + eRegNumPPC_DWARF_dbat3u = 642, + eRegNumPPC_DWARF_dbat3l = 643, + + eRegNumPPC_DWARF_hid0 = 1108, + eRegNumPPC_DWARF_hid1, + eRegNumPPC_DWARF_hid2, + eRegNumPPC_DWARF_hid3, + eRegNumPPC_DWARF_hid4, + eRegNumPPC_DWARF_hid5, + eRegNumPPC_DWARF_hid6, + eRegNumPPC_DWARF_hid7, + eRegNumPPC_DWARF_hid8, + eRegNumPPC_DWARF_hid9, + eRegNumPPC_DWARF_hid10, + eRegNumPPC_DWARF_hid11, + eRegNumPPC_DWARF_hid12, + eRegNumPPC_DWARF_hid13, + eRegNumPPC_DWARF_hid14, + eRegNumPPC_DWARF_hid15, + + eRegNumPPC_DWARF_vr0 = 1124, + eRegNumPPC_DWARF_vr1, + eRegNumPPC_DWARF_vr2, + eRegNumPPC_DWARF_vr3, + eRegNumPPC_DWARF_vr4, + eRegNumPPC_DWARF_vr5, + eRegNumPPC_DWARF_vr6, + eRegNumPPC_DWARF_vr7, + eRegNumPPC_DWARF_vr8, + eRegNumPPC_DWARF_vr9, + eRegNumPPC_DWARF_vr10, + eRegNumPPC_DWARF_vr11, + eRegNumPPC_DWARF_vr12, + eRegNumPPC_DWARF_vr13, + eRegNumPPC_DWARF_vr14, + eRegNumPPC_DWARF_vr15, + eRegNumPPC_DWARF_vr16, + eRegNumPPC_DWARF_vr17, + eRegNumPPC_DWARF_vr18, + eRegNumPPC_DWARF_vr19, + eRegNumPPC_DWARF_vr20, + eRegNumPPC_DWARF_vr21, + eRegNumPPC_DWARF_vr22, + eRegNumPPC_DWARF_vr23, + eRegNumPPC_DWARF_vr24, + eRegNumPPC_DWARF_vr25, + eRegNumPPC_DWARF_vr26, + eRegNumPPC_DWARF_vr27, + eRegNumPPC_DWARF_vr28, + eRegNumPPC_DWARF_vr29, + eRegNumPPC_DWARF_vr30, + eRegNumPPC_DWARF_vr31, + + eRegNumPPC_DWARF_ev0 = 1200, + eRegNumPPC_DWARF_ev1, + eRegNumPPC_DWARF_ev2, + eRegNumPPC_DWARF_ev3, + eRegNumPPC_DWARF_ev4, + eRegNumPPC_DWARF_ev5, + eRegNumPPC_DWARF_ev6, + eRegNumPPC_DWARF_ev7, + eRegNumPPC_DWARF_ev8, + eRegNumPPC_DWARF_ev9, + eRegNumPPC_DWARF_ev10, + eRegNumPPC_DWARF_ev11, + eRegNumPPC_DWARF_ev12, + eRegNumPPC_DWARF_ev13, + eRegNumPPC_DWARF_ev14, + eRegNumPPC_DWARF_ev15, + eRegNumPPC_DWARF_ev16, + eRegNumPPC_DWARF_ev17, + eRegNumPPC_DWARF_ev18, + eRegNumPPC_DWARF_ev19, + eRegNumPPC_DWARF_ev20, + eRegNumPPC_DWARF_ev21, + eRegNumPPC_DWARF_ev22, + eRegNumPPC_DWARF_ev23, + eRegNumPPC_DWARF_ev24, + eRegNumPPC_DWARF_ev25, + eRegNumPPC_DWARF_ev26, + eRegNumPPC_DWARF_ev27, + eRegNumPPC_DWARF_ev28, + eRegNumPPC_DWARF_ev29, + eRegNumPPC_DWARF_ev30, + eRegNumPPC_DWARF_ev31 +}; + +// Register numbering definitions for 32 and 64 bit ppc for RegisterNumberingType::GCC +enum +{ + eRegNumPPC_GCC_r0 = 0, + eRegNumPPC_GCC_r1 = 1, + eRegNumPPC_GCC_r2 = 2, + eRegNumPPC_GCC_r3 = 3, + eRegNumPPC_GCC_r4 = 4, + eRegNumPPC_GCC_r5 = 5, + eRegNumPPC_GCC_r6 = 6, + eRegNumPPC_GCC_r7 = 7, + eRegNumPPC_GCC_r8 = 8, + eRegNumPPC_GCC_r9 = 9, + eRegNumPPC_GCC_r10 = 10, + eRegNumPPC_GCC_r11 = 11, + eRegNumPPC_GCC_r12 = 12, + eRegNumPPC_GCC_r13 = 13, + eRegNumPPC_GCC_r14 = 14, + eRegNumPPC_GCC_r15 = 15, + eRegNumPPC_GCC_r16 = 16, + eRegNumPPC_GCC_r17 = 17, + eRegNumPPC_GCC_r18 = 18, + eRegNumPPC_GCC_r19 = 19, + eRegNumPPC_GCC_r20 = 20, + eRegNumPPC_GCC_r21 = 21, + eRegNumPPC_GCC_r22 = 22, + eRegNumPPC_GCC_r23 = 23, + eRegNumPPC_GCC_r24 = 24, + eRegNumPPC_GCC_r25 = 25, + eRegNumPPC_GCC_r26 = 26, + eRegNumPPC_GCC_r27 = 27, + eRegNumPPC_GCC_r28 = 28, + eRegNumPPC_GCC_r29 = 29, + eRegNumPPC_GCC_r30 = 30, + eRegNumPPC_GCC_r31 = 31, + eRegNumPPC_GCC_fr0 = 32, + eRegNumPPC_GCC_fr1 = 33, + eRegNumPPC_GCC_fr2 = 34, + eRegNumPPC_GCC_fr3 = 35, + eRegNumPPC_GCC_fr4 = 36, + eRegNumPPC_GCC_fr5 = 37, + eRegNumPPC_GCC_fr6 = 38, + eRegNumPPC_GCC_fr7 = 39, + eRegNumPPC_GCC_fr8 = 40, + eRegNumPPC_GCC_fr9 = 41, + eRegNumPPC_GCC_fr10 = 42, + eRegNumPPC_GCC_fr11 = 43, + eRegNumPPC_GCC_fr12 = 44, + eRegNumPPC_GCC_fr13 = 45, + eRegNumPPC_GCC_fr14 = 46, + eRegNumPPC_GCC_fr15 = 47, + eRegNumPPC_GCC_fr16 = 48, + eRegNumPPC_GCC_fr17 = 49, + eRegNumPPC_GCC_fr18 = 50, + eRegNumPPC_GCC_fr19 = 51, + eRegNumPPC_GCC_fr20 = 52, + eRegNumPPC_GCC_fr21 = 53, + eRegNumPPC_GCC_fr22 = 54, + eRegNumPPC_GCC_fr23 = 55, + eRegNumPPC_GCC_fr24 = 56, + eRegNumPPC_GCC_fr25 = 57, + eRegNumPPC_GCC_fr26 = 58, + eRegNumPPC_GCC_fr27 = 59, + eRegNumPPC_GCC_fr28 = 60, + eRegNumPPC_GCC_fr29 = 61, + eRegNumPPC_GCC_fr30 = 62, + eRegNumPPC_GCC_fr31 = 63, + eRegNumPPC_GCC_mq = 64, + eRegNumPPC_GCC_lr = 65, + eRegNumPPC_GCC_ctr = 66, + eRegNumPPC_GCC_ap = 67, + eRegNumPPC_GCC_cr0 = 68, + eRegNumPPC_GCC_cr1 = 69, + eRegNumPPC_GCC_cr2 = 70, + eRegNumPPC_GCC_cr3 = 71, + eRegNumPPC_GCC_cr4 = 72, + eRegNumPPC_GCC_cr5 = 73, + eRegNumPPC_GCC_cr6 = 74, + eRegNumPPC_GCC_cr7 = 75, + eRegNumPPC_GCC_xer = 76, + eRegNumPPC_GCC_v0 = 77, + eRegNumPPC_GCC_v1 = 78, + eRegNumPPC_GCC_v2 = 79, + eRegNumPPC_GCC_v3 = 80, + eRegNumPPC_GCC_v4 = 81, + eRegNumPPC_GCC_v5 = 82, + eRegNumPPC_GCC_v6 = 83, + eRegNumPPC_GCC_v7 = 84, + eRegNumPPC_GCC_v8 = 85, + eRegNumPPC_GCC_v9 = 86, + eRegNumPPC_GCC_v10 = 87, + eRegNumPPC_GCC_v11 = 88, + eRegNumPPC_GCC_v12 = 89, + eRegNumPPC_GCC_v13 = 90, + eRegNumPPC_GCC_v14 = 91, + eRegNumPPC_GCC_v15 = 92, + eRegNumPPC_GCC_v16 = 93, + eRegNumPPC_GCC_v17 = 94, + eRegNumPPC_GCC_v18 = 95, + eRegNumPPC_GCC_v19 = 96, + eRegNumPPC_GCC_v20 = 97, + eRegNumPPC_GCC_v21 = 98, + eRegNumPPC_GCC_v22 = 99, + eRegNumPPC_GCC_v23 = 100, + eRegNumPPC_GCC_v24 = 101, + eRegNumPPC_GCC_v25 = 102, + eRegNumPPC_GCC_v26 = 103, + eRegNumPPC_GCC_v27 = 104, + eRegNumPPC_GCC_v28 = 105, + eRegNumPPC_GCC_v29 = 106, + eRegNumPPC_GCC_v30 = 107, + eRegNumPPC_GCC_v31 = 108, + eRegNumPPC_GCC_vrsave = 109, + eRegNumPPC_GCC_vscr = 110, + eRegNumPPC_GCC_spe_acc = 111, + eRegNumPPC_GCC_spefscr = 112, + eRegNumPPC_GCC_sfp = 113, +}; + +static const char * g_arm_gcc_reg_names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "cc", "sfp", "afp", + "mv0", "mv1", "mv2", "mv3", "mv4", "mv5", "mv6", "mv7", + "mv8", "mv9", "mv10", "mv11", "mv12", "mv13", "mv14", "mv15", + "wcgr0","wcgr1","wcgr2","wcgr3", + "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7", + "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", + "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", + "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", + "vfpcc" +}; + +//---------------------------------------------------------------------- +// Get register names for the current object architecture given +// a register number, and a reg_kind for that register number. +//---------------------------------------------------------------------- +const char * +ArchSpec::GetRegisterName(uint32_t reg_num, uint32_t reg_kind) const +{ + return ArchSpec::GetRegisterName(m_cpu, m_sub, reg_num, reg_kind); +} + + +//---------------------------------------------------------------------- +// Get register names for the specified CPU type and subtype given +// a register number, and a reg_kind for that register number. +//---------------------------------------------------------------------- +const char * +ArchSpec::GetRegisterName(uint32_t cpu, uint32_t subtype, uint32_t reg_num, uint32_t reg_kind) +{ + if (cpu == CPU_TYPE_I386) + { + switch (reg_kind) + { + case eRegisterKindGCC: + if (reg_num < sizeof(g_i386_gcc_reg_names)/sizeof(const char *)) + return g_i386_gcc_reg_names[reg_num]; + break; + case eRegisterKindDWARF: + if (reg_num < sizeof(g_i386_dwarf_reg_names)/sizeof(const char *)) + return g_i386_dwarf_reg_names[reg_num]; + break; + default: + break; + } + } + else if (cpu == CPU_TYPE_X86_64) + { + switch (reg_kind) + { + case eRegisterKindGCC: + case eRegisterKindDWARF: + if (reg_num < sizeof(g_x86_64_dwarf_and_gcc_reg_names)/sizeof(const char *)) + return g_x86_64_dwarf_and_gcc_reg_names[reg_num]; + break; + default: + break; + } + } + else if (cpu == CPU_TYPE_ARM) + { + switch (reg_kind) + { + case eRegisterKindGCC: + if (reg_num < sizeof(g_arm_gcc_reg_names)/sizeof(const char *)) + return g_arm_gcc_reg_names[reg_num]; + break; + + case eRegisterKindDWARF: + switch (reg_num) + { + case eRegNumARM_DWARF_r0: return "r0"; + case eRegNumARM_DWARF_r1: return "r1"; + case eRegNumARM_DWARF_r2: return "r2"; + case eRegNumARM_DWARF_r3: return "r3"; + case eRegNumARM_DWARF_r4: return "r4"; + case eRegNumARM_DWARF_r5: return "r5"; + case eRegNumARM_DWARF_r6: return "r6"; + case eRegNumARM_DWARF_r7: return "r7"; + case eRegNumARM_DWARF_r8: return "r8"; + case eRegNumARM_DWARF_r9: return "r9"; + case eRegNumARM_DWARF_r10: return "r10"; + case eRegNumARM_DWARF_r11: return "r11"; + case eRegNumARM_DWARF_r12: return "r12"; + case eRegNumARM_DWARF_r13: return "sp"; + case eRegNumARM_DWARF_r14: return "lr"; + case eRegNumARM_DWARF_r15: return "pc"; + case eRegNumARM_DWARF_s0_obsolete: case eRegNumARM_DWARF_s0: return "s0"; + case eRegNumARM_DWARF_s1_obsolete: case eRegNumARM_DWARF_s1: return "s1"; + case eRegNumARM_DWARF_s2_obsolete: case eRegNumARM_DWARF_s2: return "s2"; + case eRegNumARM_DWARF_s3_obsolete: case eRegNumARM_DWARF_s3: return "s3"; + case eRegNumARM_DWARF_s4_obsolete: case eRegNumARM_DWARF_s4: return "s4"; + case eRegNumARM_DWARF_s5_obsolete: case eRegNumARM_DWARF_s5: return "s5"; + case eRegNumARM_DWARF_s6_obsolete: case eRegNumARM_DWARF_s6: return "s6"; + case eRegNumARM_DWARF_s7_obsolete: case eRegNumARM_DWARF_s7: return "s7"; + case eRegNumARM_DWARF_s8_obsolete: case eRegNumARM_DWARF_s8: return "s8"; + case eRegNumARM_DWARF_s9_obsolete: case eRegNumARM_DWARF_s9: return "s9"; + case eRegNumARM_DWARF_s10_obsolete: case eRegNumARM_DWARF_s10: return "s10"; + case eRegNumARM_DWARF_s11_obsolete: case eRegNumARM_DWARF_s11: return "s11"; + case eRegNumARM_DWARF_s12_obsolete: case eRegNumARM_DWARF_s12: return "s12"; + case eRegNumARM_DWARF_s13_obsolete: case eRegNumARM_DWARF_s13: return "s13"; + case eRegNumARM_DWARF_s14_obsolete: case eRegNumARM_DWARF_s14: return "s14"; + case eRegNumARM_DWARF_s15_obsolete: case eRegNumARM_DWARF_s15: return "s15"; + case eRegNumARM_DWARF_s16_obsolete: case eRegNumARM_DWARF_s16: return "s16"; + case eRegNumARM_DWARF_s17_obsolete: case eRegNumARM_DWARF_s17: return "s17"; + case eRegNumARM_DWARF_s18_obsolete: case eRegNumARM_DWARF_s18: return "s18"; + case eRegNumARM_DWARF_s19_obsolete: case eRegNumARM_DWARF_s19: return "s19"; + case eRegNumARM_DWARF_s20_obsolete: case eRegNumARM_DWARF_s20: return "s20"; + case eRegNumARM_DWARF_s21_obsolete: case eRegNumARM_DWARF_s21: return "s21"; + case eRegNumARM_DWARF_s22_obsolete: case eRegNumARM_DWARF_s22: return "s22"; + case eRegNumARM_DWARF_s23_obsolete: case eRegNumARM_DWARF_s23: return "s23"; + case eRegNumARM_DWARF_s24_obsolete: case eRegNumARM_DWARF_s24: return "s24"; + case eRegNumARM_DWARF_s25_obsolete: case eRegNumARM_DWARF_s25: return "s25"; + case eRegNumARM_DWARF_s26_obsolete: case eRegNumARM_DWARF_s26: return "s26"; + case eRegNumARM_DWARF_s27_obsolete: case eRegNumARM_DWARF_s27: return "s27"; + case eRegNumARM_DWARF_s28_obsolete: case eRegNumARM_DWARF_s28: return "s28"; + case eRegNumARM_DWARF_s29_obsolete: case eRegNumARM_DWARF_s29: return "s29"; + case eRegNumARM_DWARF_s30_obsolete: case eRegNumARM_DWARF_s30: return "s30"; + case eRegNumARM_DWARF_s31_obsolete: case eRegNumARM_DWARF_s31: return "s31"; + case eRegNumARM_DWARF_f0: return "f0"; + case eRegNumARM_DWARF_f1: return "f1"; + case eRegNumARM_DWARF_f2: return "f2"; + case eRegNumARM_DWARF_f3: return "f3"; + case eRegNumARM_DWARF_f4: return "f4"; + case eRegNumARM_DWARF_f5: return "f5"; + case eRegNumARM_DWARF_f6: return "f6"; + case eRegNumARM_DWARF_f7: return "f7"; + case eRegNumARM_DWARF_wCGR0: return "wCGR0/ACC0"; + case eRegNumARM_DWARF_wCGR1: return "wCGR1/ACC1"; + case eRegNumARM_DWARF_wCGR2: return "wCGR2/ACC2"; + case eRegNumARM_DWARF_wCGR3: return "wCGR3/ACC3"; + case eRegNumARM_DWARF_wCGR4: return "wCGR4/ACC4"; + case eRegNumARM_DWARF_wCGR5: return "wCGR5/ACC5"; + case eRegNumARM_DWARF_wCGR6: return "wCGR6/ACC6"; + case eRegNumARM_DWARF_wCGR7: return "wCGR7/ACC7"; + case eRegNumARM_DWARF_wR0: return "wR0"; + case eRegNumARM_DWARF_wR1: return "wR1"; + case eRegNumARM_DWARF_wR2: return "wR2"; + case eRegNumARM_DWARF_wR3: return "wR3"; + case eRegNumARM_DWARF_wR4: return "wR4"; + case eRegNumARM_DWARF_wR5: return "wR5"; + case eRegNumARM_DWARF_wR6: return "wR6"; + case eRegNumARM_DWARF_wR7: return "wR7"; + case eRegNumARM_DWARF_wR8: return "wR8"; + case eRegNumARM_DWARF_wR9: return "wR9"; + case eRegNumARM_DWARF_wR10: return "wR10"; + case eRegNumARM_DWARF_wR11: return "wR11"; + case eRegNumARM_DWARF_wR12: return "wR12"; + case eRegNumARM_DWARF_wR13: return "wR13"; + case eRegNumARM_DWARF_wR14: return "wR14"; + case eRegNumARM_DWARF_wR15: return "wR15"; + case eRegNumARM_DWARF_spsr: return "spsr"; + case eRegNumARM_DWARF_spsr_fiq: return "spsr_fiq"; + case eRegNumARM_DWARF_spsr_irq: return "spsr_irq"; + case eRegNumARM_DWARF_spsr_abt: return "spsr_abt"; + case eRegNumARM_DWARF_spsr_und: return "spsr_und"; + case eRegNumARM_DWARF_spsr_svc: return "spsr_svc"; + case eRegNumARM_DWARF_r8_usr: return "r8_usr"; + case eRegNumARM_DWARF_r9_usr: return "r9_usr"; + case eRegNumARM_DWARF_r10_usr: return "r10_usr"; + case eRegNumARM_DWARF_r11_usr: return "r11_usr"; + case eRegNumARM_DWARF_r12_usr: return "r12_usr"; + case eRegNumARM_DWARF_r13_usr: return "sp_usr"; + case eRegNumARM_DWARF_r14_usr: return "lr_usr"; + case eRegNumARM_DWARF_r8_fiq: return "r8_fiq"; + case eRegNumARM_DWARF_r9_fiq: return "r9_fiq"; + case eRegNumARM_DWARF_r10_fiq: return "r10_fiq"; + case eRegNumARM_DWARF_r11_fiq: return "r11_fiq"; + case eRegNumARM_DWARF_r12_fiq: return "r12_fiq"; + case eRegNumARM_DWARF_r13_fiq: return "sp_fiq"; + case eRegNumARM_DWARF_r14_fiq: return "lr_fiq"; + case eRegNumARM_DWARF_r13_irq: return "sp_irq"; + case eRegNumARM_DWARF_r14_irq: return "lr_irq"; + case eRegNumARM_DWARF_r13_abt: return "sp_abt"; + case eRegNumARM_DWARF_r14_abt: return "lr_abt"; + case eRegNumARM_DWARF_r13_und: return "sp_und"; + case eRegNumARM_DWARF_r14_und: return "lr_und"; + case eRegNumARM_DWARF_r13_svc: return "sp_svc"; + case eRegNumARM_DWARF_r14_svc: return "lr_svc"; + case eRegNumARM_DWARF_wC0: return "wC0"; + case eRegNumARM_DWARF_wC1: return "wC1"; + case eRegNumARM_DWARF_wC2: return "wC2"; + case eRegNumARM_DWARF_wC3: return "wC3"; + case eRegNumARM_DWARF_wC4: return "wC4"; + case eRegNumARM_DWARF_wC5: return "wC5"; + case eRegNumARM_DWARF_wC6: return "wC6"; + case eRegNumARM_DWARF_wC7: return "wC7"; + case eRegNumARM_DWARF_d0: return "d0"; + case eRegNumARM_DWARF_d1: return "d1"; + case eRegNumARM_DWARF_d2: return "d2"; + case eRegNumARM_DWARF_d3: return "d3"; + case eRegNumARM_DWARF_d4: return "d4"; + case eRegNumARM_DWARF_d5: return "d5"; + case eRegNumARM_DWARF_d6: return "d6"; + case eRegNumARM_DWARF_d7: return "d7"; + case eRegNumARM_DWARF_d8: return "d8"; + case eRegNumARM_DWARF_d9: return "d9"; + case eRegNumARM_DWARF_d10: return "d10"; + case eRegNumARM_DWARF_d11: return "d11"; + case eRegNumARM_DWARF_d12: return "d12"; + case eRegNumARM_DWARF_d13: return "d13"; + case eRegNumARM_DWARF_d14: return "d14"; + case eRegNumARM_DWARF_d15: return "d15"; + case eRegNumARM_DWARF_d16: return "d16"; + case eRegNumARM_DWARF_d17: return "d17"; + case eRegNumARM_DWARF_d18: return "d18"; + case eRegNumARM_DWARF_d19: return "d19"; + case eRegNumARM_DWARF_d20: return "d20"; + case eRegNumARM_DWARF_d21: return "d21"; + case eRegNumARM_DWARF_d22: return "d22"; + case eRegNumARM_DWARF_d23: return "d23"; + case eRegNumARM_DWARF_d24: return "d24"; + case eRegNumARM_DWARF_d25: return "d25"; + case eRegNumARM_DWARF_d26: return "d26"; + case eRegNumARM_DWARF_d27: return "d27"; + case eRegNumARM_DWARF_d28: return "d28"; + case eRegNumARM_DWARF_d29: return "d29"; + case eRegNumARM_DWARF_d30: return "d30"; + case eRegNumARM_DWARF_d31: return "d31"; + } + break; + default: + break; + } + } + else if (cpu == CPU_TYPE_POWERPC || cpu == CPU_TYPE_POWERPC64) + { + switch (reg_kind) + { + case eRegisterKindGCC: + switch (reg_num) + { + case eRegNumPPC_GCC_r0: return "r0"; + case eRegNumPPC_GCC_r1: return "r1"; + case eRegNumPPC_GCC_r2: return "r2"; + case eRegNumPPC_GCC_r3: return "r3"; + case eRegNumPPC_GCC_r4: return "r4"; + case eRegNumPPC_GCC_r5: return "r5"; + case eRegNumPPC_GCC_r6: return "r6"; + case eRegNumPPC_GCC_r7: return "r7"; + case eRegNumPPC_GCC_r8: return "r8"; + case eRegNumPPC_GCC_r9: return "r9"; + case eRegNumPPC_GCC_r10: return "r10"; + case eRegNumPPC_GCC_r11: return "r11"; + case eRegNumPPC_GCC_r12: return "r12"; + case eRegNumPPC_GCC_r13: return "r13"; + case eRegNumPPC_GCC_r14: return "r14"; + case eRegNumPPC_GCC_r15: return "r15"; + case eRegNumPPC_GCC_r16: return "r16"; + case eRegNumPPC_GCC_r17: return "r17"; + case eRegNumPPC_GCC_r18: return "r18"; + case eRegNumPPC_GCC_r19: return "r19"; + case eRegNumPPC_GCC_r20: return "r20"; + case eRegNumPPC_GCC_r21: return "r21"; + case eRegNumPPC_GCC_r22: return "r22"; + case eRegNumPPC_GCC_r23: return "r23"; + case eRegNumPPC_GCC_r24: return "r24"; + case eRegNumPPC_GCC_r25: return "r25"; + case eRegNumPPC_GCC_r26: return "r26"; + case eRegNumPPC_GCC_r27: return "r27"; + case eRegNumPPC_GCC_r28: return "r28"; + case eRegNumPPC_GCC_r29: return "r29"; + case eRegNumPPC_GCC_r30: return "r30"; + case eRegNumPPC_GCC_r31: return "r31"; + case eRegNumPPC_GCC_fr0: return "fr0"; + case eRegNumPPC_GCC_fr1: return "fr1"; + case eRegNumPPC_GCC_fr2: return "fr2"; + case eRegNumPPC_GCC_fr3: return "fr3"; + case eRegNumPPC_GCC_fr4: return "fr4"; + case eRegNumPPC_GCC_fr5: return "fr5"; + case eRegNumPPC_GCC_fr6: return "fr6"; + case eRegNumPPC_GCC_fr7: return "fr7"; + case eRegNumPPC_GCC_fr8: return "fr8"; + case eRegNumPPC_GCC_fr9: return "fr9"; + case eRegNumPPC_GCC_fr10: return "fr10"; + case eRegNumPPC_GCC_fr11: return "fr11"; + case eRegNumPPC_GCC_fr12: return "fr12"; + case eRegNumPPC_GCC_fr13: return "fr13"; + case eRegNumPPC_GCC_fr14: return "fr14"; + case eRegNumPPC_GCC_fr15: return "fr15"; + case eRegNumPPC_GCC_fr16: return "fr16"; + case eRegNumPPC_GCC_fr17: return "fr17"; + case eRegNumPPC_GCC_fr18: return "fr18"; + case eRegNumPPC_GCC_fr19: return "fr19"; + case eRegNumPPC_GCC_fr20: return "fr20"; + case eRegNumPPC_GCC_fr21: return "fr21"; + case eRegNumPPC_GCC_fr22: return "fr22"; + case eRegNumPPC_GCC_fr23: return "fr23"; + case eRegNumPPC_GCC_fr24: return "fr24"; + case eRegNumPPC_GCC_fr25: return "fr25"; + case eRegNumPPC_GCC_fr26: return "fr26"; + case eRegNumPPC_GCC_fr27: return "fr27"; + case eRegNumPPC_GCC_fr28: return "fr28"; + case eRegNumPPC_GCC_fr29: return "fr29"; + case eRegNumPPC_GCC_fr30: return "fr30"; + case eRegNumPPC_GCC_fr31: return "fr31"; + case eRegNumPPC_GCC_mq: return "mq"; + case eRegNumPPC_GCC_lr: return "lr"; + case eRegNumPPC_GCC_ctr: return "ctr"; + case eRegNumPPC_GCC_ap: return "ap"; + case eRegNumPPC_GCC_cr0: return "cr0"; + case eRegNumPPC_GCC_cr1: return "cr1"; + case eRegNumPPC_GCC_cr2: return "cr2"; + case eRegNumPPC_GCC_cr3: return "cr3"; + case eRegNumPPC_GCC_cr4: return "cr4"; + case eRegNumPPC_GCC_cr5: return "cr5"; + case eRegNumPPC_GCC_cr6: return "cr6"; + case eRegNumPPC_GCC_cr7: return "cr7"; + case eRegNumPPC_GCC_xer: return "xer"; + case eRegNumPPC_GCC_v0: return "v0"; + case eRegNumPPC_GCC_v1: return "v1"; + case eRegNumPPC_GCC_v2: return "v2"; + case eRegNumPPC_GCC_v3: return "v3"; + case eRegNumPPC_GCC_v4: return "v4"; + case eRegNumPPC_GCC_v5: return "v5"; + case eRegNumPPC_GCC_v6: return "v6"; + case eRegNumPPC_GCC_v7: return "v7"; + case eRegNumPPC_GCC_v8: return "v8"; + case eRegNumPPC_GCC_v9: return "v9"; + case eRegNumPPC_GCC_v10: return "v10"; + case eRegNumPPC_GCC_v11: return "v11"; + case eRegNumPPC_GCC_v12: return "v12"; + case eRegNumPPC_GCC_v13: return "v13"; + case eRegNumPPC_GCC_v14: return "v14"; + case eRegNumPPC_GCC_v15: return "v15"; + case eRegNumPPC_GCC_v16: return "v16"; + case eRegNumPPC_GCC_v17: return "v17"; + case eRegNumPPC_GCC_v18: return "v18"; + case eRegNumPPC_GCC_v19: return "v19"; + case eRegNumPPC_GCC_v20: return "v20"; + case eRegNumPPC_GCC_v21: return "v21"; + case eRegNumPPC_GCC_v22: return "v22"; + case eRegNumPPC_GCC_v23: return "v23"; + case eRegNumPPC_GCC_v24: return "v24"; + case eRegNumPPC_GCC_v25: return "v25"; + case eRegNumPPC_GCC_v26: return "v26"; + case eRegNumPPC_GCC_v27: return "v27"; + case eRegNumPPC_GCC_v28: return "v28"; + case eRegNumPPC_GCC_v29: return "v29"; + case eRegNumPPC_GCC_v30: return "v30"; + case eRegNumPPC_GCC_v31: return "v31"; + case eRegNumPPC_GCC_vrsave: return "vrsave"; + case eRegNumPPC_GCC_vscr: return "vscr"; + case eRegNumPPC_GCC_spe_acc: return "spe_acc"; + case eRegNumPPC_GCC_spefscr: return "spefscr"; + case eRegNumPPC_GCC_sfp: return "sfp"; + default: + break; + } + break; + + case eRegisterKindDWARF: + switch (reg_num) + { + case eRegNumPPC_DWARF_r0: return "r0"; + case eRegNumPPC_DWARF_r1: return "r1"; + case eRegNumPPC_DWARF_r2: return "r2"; + case eRegNumPPC_DWARF_r3: return "r3"; + case eRegNumPPC_DWARF_r4: return "r4"; + case eRegNumPPC_DWARF_r5: return "r5"; + case eRegNumPPC_DWARF_r6: return "r6"; + case eRegNumPPC_DWARF_r7: return "r7"; + case eRegNumPPC_DWARF_r8: return "r8"; + case eRegNumPPC_DWARF_r9: return "r9"; + case eRegNumPPC_DWARF_r10: return "r10"; + case eRegNumPPC_DWARF_r11: return "r11"; + case eRegNumPPC_DWARF_r12: return "r12"; + case eRegNumPPC_DWARF_r13: return "r13"; + case eRegNumPPC_DWARF_r14: return "r14"; + case eRegNumPPC_DWARF_r15: return "r15"; + case eRegNumPPC_DWARF_r16: return "r16"; + case eRegNumPPC_DWARF_r17: return "r17"; + case eRegNumPPC_DWARF_r18: return "r18"; + case eRegNumPPC_DWARF_r19: return "r19"; + case eRegNumPPC_DWARF_r20: return "r20"; + case eRegNumPPC_DWARF_r21: return "r21"; + case eRegNumPPC_DWARF_r22: return "r22"; + case eRegNumPPC_DWARF_r23: return "r23"; + case eRegNumPPC_DWARF_r24: return "r24"; + case eRegNumPPC_DWARF_r25: return "r25"; + case eRegNumPPC_DWARF_r26: return "r26"; + case eRegNumPPC_DWARF_r27: return "r27"; + case eRegNumPPC_DWARF_r28: return "r28"; + case eRegNumPPC_DWARF_r29: return "r29"; + case eRegNumPPC_DWARF_r30: return "r30"; + case eRegNumPPC_DWARF_r31: return "r31"; + + case eRegNumPPC_DWARF_fr0: return "fr0"; + case eRegNumPPC_DWARF_fr1: return "fr1"; + case eRegNumPPC_DWARF_fr2: return "fr2"; + case eRegNumPPC_DWARF_fr3: return "fr3"; + case eRegNumPPC_DWARF_fr4: return "fr4"; + case eRegNumPPC_DWARF_fr5: return "fr5"; + case eRegNumPPC_DWARF_fr6: return "fr6"; + case eRegNumPPC_DWARF_fr7: return "fr7"; + case eRegNumPPC_DWARF_fr8: return "fr8"; + case eRegNumPPC_DWARF_fr9: return "fr9"; + case eRegNumPPC_DWARF_fr10: return "fr10"; + case eRegNumPPC_DWARF_fr11: return "fr11"; + case eRegNumPPC_DWARF_fr12: return "fr12"; + case eRegNumPPC_DWARF_fr13: return "fr13"; + case eRegNumPPC_DWARF_fr14: return "fr14"; + case eRegNumPPC_DWARF_fr15: return "fr15"; + case eRegNumPPC_DWARF_fr16: return "fr16"; + case eRegNumPPC_DWARF_fr17: return "fr17"; + case eRegNumPPC_DWARF_fr18: return "fr18"; + case eRegNumPPC_DWARF_fr19: return "fr19"; + case eRegNumPPC_DWARF_fr20: return "fr20"; + case eRegNumPPC_DWARF_fr21: return "fr21"; + case eRegNumPPC_DWARF_fr22: return "fr22"; + case eRegNumPPC_DWARF_fr23: return "fr23"; + case eRegNumPPC_DWARF_fr24: return "fr24"; + case eRegNumPPC_DWARF_fr25: return "fr25"; + case eRegNumPPC_DWARF_fr26: return "fr26"; + case eRegNumPPC_DWARF_fr27: return "fr27"; + case eRegNumPPC_DWARF_fr28: return "fr28"; + case eRegNumPPC_DWARF_fr29: return "fr29"; + case eRegNumPPC_DWARF_fr30: return "fr30"; + case eRegNumPPC_DWARF_fr31: return "fr31"; + + case eRegNumPPC_DWARF_cr: return "cr"; + case eRegNumPPC_DWARF_fpscr: return "fpscr"; + case eRegNumPPC_DWARF_msr: return "msr"; + case eRegNumPPC_DWARF_vscr: return "vscr"; + + case eRegNumPPC_DWARF_sr0: return "sr0"; + case eRegNumPPC_DWARF_sr1: return "sr1"; + case eRegNumPPC_DWARF_sr2: return "sr2"; + case eRegNumPPC_DWARF_sr3: return "sr3"; + case eRegNumPPC_DWARF_sr4: return "sr4"; + case eRegNumPPC_DWARF_sr5: return "sr5"; + case eRegNumPPC_DWARF_sr6: return "sr6"; + case eRegNumPPC_DWARF_sr7: return "sr7"; + case eRegNumPPC_DWARF_sr8: return "sr8"; + case eRegNumPPC_DWARF_sr9: return "sr9"; + case eRegNumPPC_DWARF_sr10: return "sr10"; + case eRegNumPPC_DWARF_sr11: return "sr11"; + case eRegNumPPC_DWARF_sr12: return "sr12"; + case eRegNumPPC_DWARF_sr13: return "sr13"; + case eRegNumPPC_DWARF_sr14: return "sr14"; + case eRegNumPPC_DWARF_sr15: return "sr15"; + + case eRegNumPPC_DWARF_acc: return "acc"; + case eRegNumPPC_DWARF_mq: return "mq"; + case eRegNumPPC_DWARF_xer: return "xer"; + case eRegNumPPC_DWARF_rtcu: return "rtcu"; + case eRegNumPPC_DWARF_rtcl: return "rtcl"; + + case eRegNumPPC_DWARF_lr: return "lr"; + case eRegNumPPC_DWARF_ctr: return "ctr"; + + case eRegNumPPC_DWARF_dsisr: return "dsisr"; + case eRegNumPPC_DWARF_dar: return "dar"; + case eRegNumPPC_DWARF_dec: return "dec"; + case eRegNumPPC_DWARF_sdr1: return "sdr1"; + case eRegNumPPC_DWARF_srr0: return "srr0"; + case eRegNumPPC_DWARF_srr1: return "srr1"; + + case eRegNumPPC_DWARF_vrsave: return "vrsave"; + + case eRegNumPPC_DWARF_sprg0: return "sprg0"; + case eRegNumPPC_DWARF_sprg1: return "sprg1"; + case eRegNumPPC_DWARF_sprg2: return "sprg2"; + case eRegNumPPC_DWARF_sprg3: return "sprg3"; + + case eRegNumPPC_DWARF_asr: return "asr"; + case eRegNumPPC_DWARF_ear: return "ear"; + case eRegNumPPC_DWARF_tb: return "tb"; + case eRegNumPPC_DWARF_tbu: return "tbu"; + case eRegNumPPC_DWARF_pvr: return "pvr"; + + case eRegNumPPC_DWARF_spefscr: return "spefscr"; + + case eRegNumPPC_DWARF_ibat0u: return "ibat0u"; + case eRegNumPPC_DWARF_ibat0l: return "ibat0l"; + case eRegNumPPC_DWARF_ibat1u: return "ibat1u"; + case eRegNumPPC_DWARF_ibat1l: return "ibat1l"; + case eRegNumPPC_DWARF_ibat2u: return "ibat2u"; + case eRegNumPPC_DWARF_ibat2l: return "ibat2l"; + case eRegNumPPC_DWARF_ibat3u: return "ibat3u"; + case eRegNumPPC_DWARF_ibat3l: return "ibat3l"; + case eRegNumPPC_DWARF_dbat0u: return "dbat0u"; + case eRegNumPPC_DWARF_dbat0l: return "dbat0l"; + case eRegNumPPC_DWARF_dbat1u: return "dbat1u"; + case eRegNumPPC_DWARF_dbat1l: return "dbat1l"; + case eRegNumPPC_DWARF_dbat2u: return "dbat2u"; + case eRegNumPPC_DWARF_dbat2l: return "dbat2l"; + case eRegNumPPC_DWARF_dbat3u: return "dbat3u"; + case eRegNumPPC_DWARF_dbat3l: return "dbat3l"; + + case eRegNumPPC_DWARF_hid0: return "hid0"; + case eRegNumPPC_DWARF_hid1: return "hid1"; + case eRegNumPPC_DWARF_hid2: return "hid2"; + case eRegNumPPC_DWARF_hid3: return "hid3"; + case eRegNumPPC_DWARF_hid4: return "hid4"; + case eRegNumPPC_DWARF_hid5: return "hid5"; + case eRegNumPPC_DWARF_hid6: return "hid6"; + case eRegNumPPC_DWARF_hid7: return "hid7"; + case eRegNumPPC_DWARF_hid8: return "hid8"; + case eRegNumPPC_DWARF_hid9: return "hid9"; + case eRegNumPPC_DWARF_hid10: return "hid10"; + case eRegNumPPC_DWARF_hid11: return "hid11"; + case eRegNumPPC_DWARF_hid12: return "hid12"; + case eRegNumPPC_DWARF_hid13: return "hid13"; + case eRegNumPPC_DWARF_hid14: return "hid14"; + case eRegNumPPC_DWARF_hid15: return "hid15"; + + case eRegNumPPC_DWARF_vr0: return "vr0"; + case eRegNumPPC_DWARF_vr1: return "vr1"; + case eRegNumPPC_DWARF_vr2: return "vr2"; + case eRegNumPPC_DWARF_vr3: return "vr3"; + case eRegNumPPC_DWARF_vr4: return "vr4"; + case eRegNumPPC_DWARF_vr5: return "vr5"; + case eRegNumPPC_DWARF_vr6: return "vr6"; + case eRegNumPPC_DWARF_vr7: return "vr7"; + case eRegNumPPC_DWARF_vr8: return "vr8"; + case eRegNumPPC_DWARF_vr9: return "vr9"; + case eRegNumPPC_DWARF_vr10: return "vr10"; + case eRegNumPPC_DWARF_vr11: return "vr11"; + case eRegNumPPC_DWARF_vr12: return "vr12"; + case eRegNumPPC_DWARF_vr13: return "vr13"; + case eRegNumPPC_DWARF_vr14: return "vr14"; + case eRegNumPPC_DWARF_vr15: return "vr15"; + case eRegNumPPC_DWARF_vr16: return "vr16"; + case eRegNumPPC_DWARF_vr17: return "vr17"; + case eRegNumPPC_DWARF_vr18: return "vr18"; + case eRegNumPPC_DWARF_vr19: return "vr19"; + case eRegNumPPC_DWARF_vr20: return "vr20"; + case eRegNumPPC_DWARF_vr21: return "vr21"; + case eRegNumPPC_DWARF_vr22: return "vr22"; + case eRegNumPPC_DWARF_vr23: return "vr23"; + case eRegNumPPC_DWARF_vr24: return "vr24"; + case eRegNumPPC_DWARF_vr25: return "vr25"; + case eRegNumPPC_DWARF_vr26: return "vr26"; + case eRegNumPPC_DWARF_vr27: return "vr27"; + case eRegNumPPC_DWARF_vr28: return "vr28"; + case eRegNumPPC_DWARF_vr29: return "vr29"; + case eRegNumPPC_DWARF_vr30: return "vr30"; + case eRegNumPPC_DWARF_vr31: return "vr31"; + + case eRegNumPPC_DWARF_ev0: return "ev0"; + case eRegNumPPC_DWARF_ev1: return "ev1"; + case eRegNumPPC_DWARF_ev2: return "ev2"; + case eRegNumPPC_DWARF_ev3: return "ev3"; + case eRegNumPPC_DWARF_ev4: return "ev4"; + case eRegNumPPC_DWARF_ev5: return "ev5"; + case eRegNumPPC_DWARF_ev6: return "ev6"; + case eRegNumPPC_DWARF_ev7: return "ev7"; + case eRegNumPPC_DWARF_ev8: return "ev8"; + case eRegNumPPC_DWARF_ev9: return "ev9"; + case eRegNumPPC_DWARF_ev10: return "ev10"; + case eRegNumPPC_DWARF_ev11: return "ev11"; + case eRegNumPPC_DWARF_ev12: return "ev12"; + case eRegNumPPC_DWARF_ev13: return "ev13"; + case eRegNumPPC_DWARF_ev14: return "ev14"; + case eRegNumPPC_DWARF_ev15: return "ev15"; + case eRegNumPPC_DWARF_ev16: return "ev16"; + case eRegNumPPC_DWARF_ev17: return "ev17"; + case eRegNumPPC_DWARF_ev18: return "ev18"; + case eRegNumPPC_DWARF_ev19: return "ev19"; + case eRegNumPPC_DWARF_ev20: return "ev20"; + case eRegNumPPC_DWARF_ev21: return "ev21"; + case eRegNumPPC_DWARF_ev22: return "ev22"; + case eRegNumPPC_DWARF_ev23: return "ev23"; + case eRegNumPPC_DWARF_ev24: return "ev24"; + case eRegNumPPC_DWARF_ev25: return "ev25"; + case eRegNumPPC_DWARF_ev26: return "ev26"; + case eRegNumPPC_DWARF_ev27: return "ev27"; + case eRegNumPPC_DWARF_ev28: return "ev28"; + case eRegNumPPC_DWARF_ev29: return "ev29"; + case eRegNumPPC_DWARF_ev30: return "ev30"; + case eRegNumPPC_DWARF_ev31: return "ev31"; + default: + break; + } + break; + default: + break; + } + + } + return NULL; +} + +//---------------------------------------------------------------------- +// Returns true if this object contains a valid architecture, false +// otherwise. +//---------------------------------------------------------------------- +bool +ArchSpec::IsValid() const +{ + return !(m_cpu == LLDB_INVALID_CPUTYPE); +} + +//---------------------------------------------------------------------- +// Returns true if this architecture is 64 bit, otherwise 32 bit is +// assumed and false is returned. +//---------------------------------------------------------------------- +uint32_t +ArchSpec::GetAddressByteSize() const +{ + if (GetCPUType() & CPU_ARCH_ABI64) + return 8; + return 4; +} + +//---------------------------------------------------------------------- +// Returns the number of bytes that this object takes when an +// instance exists in memory. +//---------------------------------------------------------------------- +size_t +ArchSpec::MemorySize() const +{ + return sizeof(ArchSpec); +} + +bool +ArchSpec::SetArchFromTargetTriple (const char *target_triple) +{ + if (target_triple) + { + const char *hyphen = strchr(target_triple, '-'); + if (hyphen) + { + std::string arch_only (target_triple, hyphen); + return SetArch (arch_only.c_str()); + } + } + return SetArch (target_triple); +} + +//---------------------------------------------------------------------- +// Change the CPU type and subtype given an architecture name. +//---------------------------------------------------------------------- +bool +ArchSpec::SetArch(const char *arch_name) +{ + if (arch_name && arch_name[0] != '\0') + { + size_t i; + // Search for ARCH_NAME in our architecture definitions structure + for (i=0; i<k_num_arch_defs; ++i) + { + if (strcasecmp(arch_name, g_arch_defs[i].name) == 0) + { + // we found a match + m_cpu = g_arch_defs[i].cpu; + m_sub = g_arch_defs[i].sub; + return true; + } + } + + + const char *str = arch_name; + char *end = NULL; + // Check for a numeric cpu followed by an optional '.' and numeric subtype. + // This allows for support of new cpu type/subtypes without having to have + // a recompiled debug core. + // Examples: + // "12.6" is armv6 + // "0x0000000c.0x00000006" is also armv6 + m_cpu = strtoul(str, &end, 0); + if (str != end) + { + if (*end == '.') + { + // We have a cputype.cpusubtype format + str = end + 1; + if (*str != '\0') + { + m_sub = strtoul(str, &end, 0); + if (*end == '\0') + { + // We consumed the entire string and got a cpu type and subtype + return true; + } + } + } + + // If we reach this point we have a valid cpu type, but no cpu subtype. + // Search for the first matching cpu type and use the corresponding cpu + // subtype. This setting should typically be the _ALL variant and should + // appear first in the list for each cpu type in the g_arch_defs + // structure. + for (i=0; i<k_num_arch_defs; ++i) + { + if (m_cpu == g_arch_defs[i].cpu) + { + m_sub = g_arch_defs[i].sub; + return true; + } + } + + // Default the cpu subtype to zero when we don't have a matching + // cpu type in our architecture defs structure (g_arch_defs). + m_sub = 0; + return true; + + } + } + m_cpu = LLDB_INVALID_CPUTYPE; + m_sub = 0; + return false; +} + +//---------------------------------------------------------------------- +// CPU type and subtype set accessor. +//---------------------------------------------------------------------- +void +ArchSpec::SetArch (uint32_t cpu_type, uint32_t cpu_subtype) +{ + m_cpu = cpu_type; + m_sub = cpu_subtype; +} + +//---------------------------------------------------------------------- +// CPU type set accessor. +//---------------------------------------------------------------------- +void +ArchSpec::SetCPUType (uint32_t cpu) +{ + m_cpu = cpu; +} + +//---------------------------------------------------------------------- +// CPU subtype set accessor. +//---------------------------------------------------------------------- +void +ArchSpec::SetCPUSubtype (uint32_t subtype) +{ + m_sub = subtype; +} + +ByteOrder +ArchSpec::GetDefaultEndian () const +{ + switch (m_cpu) + { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return eByteOrderBig; + + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + return eByteOrderLittle; + + default: + break; + } + return eByteOrderInvalid; +} + +//---------------------------------------------------------------------- +// Equal operator +//---------------------------------------------------------------------- +bool +lldb_private::operator== (const ArchSpec& lhs, const ArchSpec& rhs) +{ + uint32_t lhs_cpu = lhs.GetCPUType(); + uint32_t rhs_cpu = rhs.GetCPUType(); + + if (lhs_cpu == CPU_TYPE_ANY || rhs_cpu == CPU_TYPE_ANY) + return true; + + else if (lhs_cpu == rhs_cpu) + { + uint32_t lhs_subtype = lhs.GetCPUSubtype(); + uint32_t rhs_subtype = rhs.GetCPUSubtype(); + if (lhs_subtype == CPU_TYPE_ANY || rhs_subtype == CPU_TYPE_ANY) + return true; + return lhs_subtype == rhs_subtype; + } + return false; +} + + +//---------------------------------------------------------------------- +// Not Equal operator +//---------------------------------------------------------------------- +bool +lldb_private::operator!= (const ArchSpec& lhs, const ArchSpec& rhs) +{ + return !(lhs == rhs); +} + +//---------------------------------------------------------------------- +// Less than operator +//---------------------------------------------------------------------- +bool +lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs) +{ + uint32_t lhs_cpu = lhs.GetCPUType(); + uint32_t rhs_cpu = rhs.GetCPUType(); + + if (lhs_cpu == rhs_cpu) + return lhs.GetCPUSubtype() < rhs.GetCPUSubtype(); + + return lhs_cpu < rhs_cpu; +} + diff --git a/lldb/source/Core/Args.cpp b/lldb/source/Core/Args.cpp new file mode 100644 index 00000000000..7cb137555d1 --- /dev/null +++ b/lldb/source/Core/Args.cpp @@ -0,0 +1,1179 @@ +//===-- Args.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 <getopt.h> +#include <histedit.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Args.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +namespace lldb_private { + +class Tokenizer +{ +public: + Tokenizer (const char *separator_chars = NULL) : + m_tokenizer(NULL) + { + m_tokenizer = ::tok_init (separator_chars); + } + + ~Tokenizer () + { + if (m_tokenizer) + { + ::tok_end (m_tokenizer); + m_tokenizer = NULL; + } + } + + void + Reset () + { + assert (m_tokenizer); + ::tok_reset (m_tokenizer); + } + + int + TokenizeLineInfo (const ::LineInfo *line_info) + { + assert (m_tokenizer); + return ::tok_line (m_tokenizer, + line_info, + &m_argc, + &m_argv, + &m_cursor_arg_index, + &m_cursor_arg_offset); + } + + int + TokenizeCString (const char *cstr) + { + assert (m_tokenizer); + m_cursor_arg_index = -1; + m_cursor_arg_offset = -1; + return ::tok_str (m_tokenizer, + cstr, + &m_argc, + &m_argv); + } + + + int + GetArgCount () const + { + return m_argc; + } + + const char ** + GetArgVector () const + { + return m_argv; + } + + int + GetCursoreArgIndex () const + { + return m_cursor_arg_index; + } + + int + GetCursoreArgOffset () const + { + return m_cursor_arg_offset; + } + + +protected: + struct tokenizer* m_tokenizer; + const char **m_argv; + int m_argc; + int m_cursor_arg_index; + int m_cursor_arg_offset; +}; + +} // namespace lldb_private + +using namespace lldb; +using namespace lldb_private; + +static const char *k_space_characters = "\t\n\v\f\r "; +static const char *k_space_characters_with_slash = "\t\n\v\f\r \\"; + + +//---------------------------------------------------------------------- +// Args constructor +//---------------------------------------------------------------------- +Args::Args (const char *command) : + m_args(), + m_argv() +{ + SetCommandString (command); +} + + +Args::Args (const char *command, size_t len) : + m_args(), + m_argv() +{ + SetCommandString (command, len); +} + + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Args::~Args () +{ +} + +void +Args::Dump (Stream *s) +{ +// int argc = GetArgumentCount(); +// +// arg_sstr_collection::const_iterator pos, begin = m_args.begin(), end = m_args.end(); +// for (pos = m_args.begin(); pos != end; ++pos) +// { +// s->Indent(); +// s->Printf("args[%zu]=%s\n", std::distance(begin, pos), pos->c_str()); +// } +// s->EOL(); + const int argc = m_argv.size(); + for (int i=0; i<argc; ++i) + { + s->Indent(); + const char *arg_cstr = m_argv[i]; + if (arg_cstr) + s->Printf("argv[%i]=\"%s\"\n", i, arg_cstr); + else + s->Printf("argv[%i]=NULL\n", i); + } + s->EOL(); +} + +bool +Args::GetCommandString (std::string &command) +{ + command.clear(); + int argc = GetArgumentCount(); + for (int i=0; i<argc; ++i) + { + if (i > 0) + command += ' '; + command += m_argv[i]; + } + return argc > 0; +} + +void +Args::SetCommandString (const char *command, size_t len) +{ + // Use std::string to make sure we get a NULL terminated string we can use + // as "command" could point to a string within a large string.... + std::string null_terminated_command(command, len); + SetCommandString(null_terminated_command.c_str()); +} + +void +Args::SetCommandString (const char *command) +{ + m_args.clear(); + m_argv.clear(); + if (command && command[0]) + { + const char *arg_start; + const char *next_arg_start; + for (arg_start = command, next_arg_start = NULL; + arg_start && arg_start[0]; + arg_start = next_arg_start, next_arg_start = NULL) + { + // Skip any leading space characters + arg_start = ::strspn (arg_start, k_space_characters) + arg_start; + + // If there were only space characters to the end of the line, then + // we're done. + if (*arg_start == '\0') + break; + + std::string arg; + const char *arg_end = NULL; + + switch (*arg_start) + { + case '\'': + case '"': + case '`': + { + // Look for either a quote character, or the backslash + // character + const char quote_char = *arg_start; + char find_chars[3] = { quote_char, '\\' , '\0'}; + bool is_backtick = (quote_char == '`'); + if (quote_char == '"' || quote_char == '`') + m_args_quote_char.push_back(quote_char); + else + m_args_quote_char.push_back('\0'); + + while (*arg_start != '\0') + { + arg_end = ::strcspn (arg_start + 1, find_chars) + arg_start + 1; + + if (*arg_end == '\0') + { + arg.append (arg_start); + break; + } + + // Watch out for quote characters prefixed with '\' + if (*arg_end == '\\') + { + if (arg_end[1] == quote_char) + { + // The character following the '\' is our quote + // character so strip the backslash character + arg.append (arg_start, arg_end); + } + else + { + // The character following the '\' is NOT our + // quote character, so include the backslash + // and continue + arg.append (arg_start, arg_end + 1); + } + arg_start = arg_end + 1; + continue; + } + else + { + arg.append (arg_start, arg_end + 1); + next_arg_start = arg_end + 1; + break; + } + } + + // Skip single and double quotes, but leave backtick quotes + if (!is_backtick) + { + char first_c = arg[0]; + arg.erase(0,1); + // Only erase the last character if it is the same as the first. + // Otherwise, we're parsing an incomplete command line, and we + // would be stripping off the last character of that string. + if (arg[arg.size() - 1] == first_c) + arg.erase(arg.size() - 1, 1); + } + } + break; + default: + { + m_args_quote_char.push_back('\0'); + // Look for the next non-escaped space character + while (*arg_start != '\0') + { + arg_end = ::strcspn (arg_start, k_space_characters_with_slash) + arg_start; + + if (arg_end == NULL) + { + arg.append(arg_start); + break; + } + + if (*arg_end == '\\') + { + // Append up to the '\' char + arg.append (arg_start, arg_end); + + if (arg_end[1] == '\0') + break; + + // Append the character following the '\' if it isn't + // the end of the string + arg.append (1, arg_end[1]); + arg_start = arg_end + 2; + continue; + } + else + { + arg.append (arg_start, arg_end); + next_arg_start = arg_end; + break; + } + } + } + break; + } + + m_args.push_back(arg); + } + } + UpdateArgvFromArgs(); +} + +void +Args::UpdateArgsAfterOptionParsing() +{ + // Now m_argv might be out of date with m_args, so we need to fix that + arg_cstr_collection::const_iterator argv_pos, argv_end = m_argv.end(); + arg_sstr_collection::iterator args_pos; + arg_quote_char_collection::iterator quotes_pos; + + for (argv_pos = m_argv.begin(), args_pos = m_args.begin(), quotes_pos = m_args_quote_char.begin(); + argv_pos != argv_end && args_pos != m_args.end(); + ++argv_pos) + { + const char *argv_cstr = *argv_pos; + if (argv_cstr == NULL) + break; + + while (args_pos != m_args.end()) + { + const char *args_cstr = args_pos->c_str(); + if (args_cstr == argv_cstr) + { + // We found the argument that matches the C string in the + // vector, so we can now look for the next one + ++args_pos; + ++quotes_pos; + break; + } + else + { + quotes_pos = m_args_quote_char.erase (quotes_pos); + args_pos = m_args.erase (args_pos); + } + } + } + + if (args_pos != m_args.end()) + m_args.erase (args_pos, m_args.end()); + + if (quotes_pos != m_args_quote_char.end()) + m_args_quote_char.erase (quotes_pos, m_args_quote_char.end()); +} + +void +Args::UpdateArgvFromArgs() +{ + m_argv.clear(); + arg_sstr_collection::const_iterator pos, end = m_args.end(); + for (pos = m_args.begin(); pos != end; ++pos) + m_argv.push_back(pos->c_str()); + m_argv.push_back(NULL); +} + +size_t +Args::GetArgumentCount() const +{ + if (m_argv.empty()) + return 0; + return m_argv.size() - 1; +} + +const char * +Args::GetArgumentAtIndex (size_t idx) const +{ + if (idx < m_argv.size()) + return m_argv[idx]; + return NULL; +} + +char +Args::GetArgumentQuoteCharAtIndex (size_t idx) const +{ + if (idx < m_args_quote_char.size()) + return m_args_quote_char[idx]; + return '\0'; +} + +char ** +Args::GetArgumentVector() +{ + if (!m_argv.empty()) + return (char **)&m_argv[0]; + return NULL; +} + +const char ** +Args::GetConstArgumentVector() const +{ + if (!m_argv.empty()) + return (const char **)&m_argv[0]; + return NULL; +} + +void +Args::Shift () +{ + // Don't pop the last NULL terminator from the argv array + if (m_argv.size() > 1) + { + m_argv.erase(m_argv.begin()); + m_args.pop_front(); + m_args_quote_char.erase(m_args_quote_char.begin()); + } +} + +const char * +Args::Unshift (const char *arg_cstr, char quote_char) +{ + m_args.push_front(arg_cstr); + m_argv.insert(m_argv.begin(), m_args.front().c_str()); + m_args_quote_char.insert(m_args_quote_char.begin(), quote_char); + return GetArgumentAtIndex (0); +} + +void +Args::AppendArguments (const Args &rhs) +{ + const size_t rhs_argc = rhs.GetArgumentCount(); + for (size_t i=0; i<rhs_argc; ++i) + AppendArgument(rhs.GetArgumentAtIndex(i)); +} + +const char * +Args::AppendArgument (const char *arg_cstr, char quote_char) +{ + return InsertArgumentAtIndex (GetArgumentCount(), arg_cstr, quote_char); +} + +const char * +Args::InsertArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + pos = m_args.insert(pos, arg_cstr); + + + m_args_quote_char.insert(m_args_quote_char.begin() + idx, quote_char); + + UpdateArgvFromArgs(); + return GetArgumentAtIndex(idx); +} + +const char * +Args::ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + pos->assign(arg_cstr); + assert(idx < m_argv.size() - 1); + m_argv[idx] = pos->c_str(); + m_args_quote_char[idx] = quote_char; + return GetArgumentAtIndex(idx); + } + return NULL; +} + +void +Args::DeleteArgumentAtIndex (size_t idx) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + m_args.erase (pos); + assert(idx < m_argv.size() - 1); + m_argv.erase(m_argv.begin() + idx); + m_args_quote_char.erase(m_args_quote_char.begin() + idx); + } +} + +void +Args::SetArguments (int argc, const char **argv) +{ + // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is + // no need to clear it here. + m_args.clear(); + m_args_quote_char.clear(); + + // Make a copy of the arguments in our internal buffer + size_t i; + // First copy each string + for (i=0; i<argc; ++i) + { + m_args.push_back (argv[i]); + if ((argv[i][0] == '"') || (argv[i][0] == '`')) + m_args_quote_char.push_back (argv[i][0]); + else + m_args_quote_char.push_back ('\0'); + } + + UpdateArgvFromArgs(); +} + + +Error +Args::ParseOptions (Options &options) +{ + StreamString sstr; + int i; + Error error; + struct option *long_options = options.GetLongOptions(); + if (long_options == NULL) + { + error.SetErrorStringWithFormat("Invalid long options.\n"); + return error; + } + + for (i=0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char)long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: break; + case required_argument: sstr << ':'; break; + case optional_argument: sstr << "::"; break; + } + } + } + optreset = 1; + optind = 1; + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long(GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options, + &long_options_index); + if (val == -1) + break; + + // Did we get an error? + if (val == '?') + { + error.SetErrorStringWithFormat("Unknown or ambiguous option.\n"); + break; + } + // The option auto-set itself + if (val == 0) + continue; + + ((Options *) &options)->OptionSeen (val); + + // Lookup the long option index + if (long_options_index == -1) + { + for (int i=0; + long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; + ++i) + { + if (long_options[i].val == val) + { + long_options_index = i; + break; + } + } + } + // Call the callback with the option + if (long_options_index >= 0) + { + error = options.SetOptionValue(long_options_index, + long_options[long_options_index].has_arg == no_argument ? NULL : optarg); + } + else + { + error.SetErrorStringWithFormat("Invalid option with value '%i'.\n", val); + } + if (error.Fail()) + break; + } + + // Update our ARGV now that get options has consumed all the options + m_argv.erase(m_argv.begin(), m_argv.begin() + optind); + UpdateArgsAfterOptionParsing (); + return error; +} + +void +Args::Clear () +{ + m_args.clear (); + m_argv.clear (); + m_args_quote_char.clear(); +} + +int32_t +Args::StringToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + int32_t uval = ::strtol (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +uint32_t +Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + uint32_t uval = ::strtoul (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + + +int64_t +Args::StringToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + int64_t uval = ::strtoll (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +uint64_t +Args::StringToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + uint64_t uval = ::strtoull (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +lldb::addr_t +Args::StringToAddress (const char *s, lldb::addr_t fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + lldb::addr_t addr = ::strtoull (s, &end, 0); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return addr; // All characters were used, return the result + } + // Try base 16 with no prefix... + addr = ::strtoull (s, &end, 16); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return addr; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +bool +Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if (::strcasecmp (s, "false") == 0 || + ::strcasecmp (s, "off") == 0 || + ::strcasecmp (s, "no") == 0 || + ::strcmp (s, "0") == 0) + { + if (success_ptr) + *success_ptr = true; + return false; + } + else + if (::strcasecmp (s, "true") == 0 || + ::strcasecmp (s, "on") == 0 || + ::strcasecmp (s, "yes") == 0 || + ::strcmp (s, "1") == 0) + { + if (success_ptr) *success_ptr = true; + return true; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +int32_t +Args::StringToOptionEnum (const char *s, lldb::OptionEnumValueElement *enum_values, int32_t fail_value, bool *success_ptr) +{ + if (enum_values && s && s[0]) + { + for (int i = 0; enum_values[i].string_value != NULL ; i++) + { + if (strstr(enum_values[i].string_value, s) == enum_values[i].string_value) + { + if (success_ptr) *success_ptr = true; + return enum_values[i].value; + } + } + } + if (success_ptr) *success_ptr = false; + + return fail_value; +} + +ScriptLanguage +Args::StringToScriptLanguage (const char *s, ScriptLanguage fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if ((::strcasecmp (s, "python") == 0) || + (::strcasecmp (s, "default") == 0 && eScriptLanguagePython == eScriptLanguageDefault)) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguagePython; + } + if (::strcasecmp (s, "none")) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguageNone; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +Error +Args::StringToFormat +( + const char *s, + lldb::Format &format +) +{ + format = eFormatInvalid; + Error error; + + if (s && s[0]) + { + switch (s[0]) + { + case 'y': format = eFormatBytes; break; + case 'Y': format = eFormatBytesWithASCII; break; + case 'b': format = eFormatBinary; break; + case 'B': format = eFormatBoolean; break; + case 'c': format = eFormatChar; break; + case 'C': format = eFormatCharPrintable; break; + case 'o': format = eFormatOctal; break; + case 'i': + case 'd': format = eFormatDecimal; break; + case 'u': format = eFormatUnsigned; break; + case 'x': format = eFormatHex; break; + case 'f': + case 'e': + case 'g': format = eFormatFloat; break; + case 'p': format = eFormatPointer; break; + case 's': format = eFormatCString; break; + default: + error.SetErrorStringWithFormat("Invalid format character '%c'. Valid values are:\n" + " b - binary\n" + " B - boolean\n" + " c - char\n" + " C - printable char\n" + " d - signed decimal\n" + " e - float\n" + " f - float\n" + " g - float\n" + " i - signed decimal\n" + " o - octal\n" + " s - c-string\n" + " u - unsigned decimal\n" + " x - hex\n" + " y - bytes\n" + " Y - bytes with ASCII\n", s[0]); + break; + } + + if (error.Fail()) + return error; + } + else + { + error.SetErrorStringWithFormat("%s option string.\n", s ? "empty" : "invalid"); + } + return error; +} + +void +Args::LongestCommonPrefix (std::string &common_prefix) +{ + arg_sstr_collection::iterator pos, end = m_args.end(); + pos = m_args.begin(); + if (pos == end) + common_prefix.clear(); + else + common_prefix = (*pos); + + for (++pos; pos != end; ++pos) + { + int new_size = (*pos).size(); + + // 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 (int i = 0; i < common_prefix.size(); i++) + { + if ((*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 +Args::ParseAliasOptions +( + Options &options, + CommandReturnObject &result, + OptionArgVector *option_arg_vector +) +{ + StreamString sstr; + int i; + struct option *long_options = options.GetLongOptions(); + + if (long_options == NULL) + { + result.AppendError ("invalid long options"); + result.SetStatus (eReturnStatusFailed); + return; + } + + for (i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + + optreset = 1; + optind = 1; + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long (GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options, + &long_options_index); + + if (val == -1) + break; + + if (val == '?') + { + result.AppendError ("unknown or ambiguous option"); + result.SetStatus (eReturnStatusFailed); + break; + } + + if (val == 0) + continue; + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; + ++j) + { + if (long_options[j].val == val) + { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) + { + StreamString option_str; + option_str.Printf ("-%c", (char) val); + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), "<no-argument>")); + break; + case required_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + std::string (optarg))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n", + option_str.GetData()); + result.SetStatus (eReturnStatusFailed); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + std::string (optarg))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + "<no-argument>")); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + break; + default: + result.AppendErrorWithFormat + ("error with options table; invalid value in has_arg field for option '%c'.\n", + (char) val); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", (char) val); + result.SetStatus (eReturnStatusFailed); + } + if (!result.Succeeded()) + break; + } +} + +void +Args::ParseArgsForCompletion +( + Options &options, + OptionElementVector &option_element_vector +) +{ + StreamString sstr; + int i; + struct option *long_options = options.GetLongOptions(); + option_element_vector.clear(); + + if (long_options == NULL) + { + return; + } + + // Leading : tells getopt to return a : for a missing option argument AND + // to suppress error messages. + + sstr << ":"; + for (i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + + optreset = 1; + optind = 1; + opterr = 0; + + int val; + const OptionDefinition *opt_defs = options.GetDefinitions(); + + // Fooey... getopt_long permutes the GetArgumentVector for no apparent reason. + // So we have to build another Arg and pass that to getopt_long so it doesn't + // screw up the one we have. + + std::vector<const char *> dummy_vec(GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); + + while (1) + { + bool missing_argument = false; + int parse_start = optind; + int long_options_index = -1; + val = ::getopt_long (dummy_vec.size() - 1,(char *const *) dummy_vec.data(), sstr.GetData(), long_options, + &long_options_index); + + if (val == -1) + break; + + else if (val == '?') + { + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + continue; + } + else if (val == 0) + { + continue; + } + else if (val == ':') + { + // This is a missing argument. + val = optopt; + missing_argument = true; + } + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; + ++j) + { + if (long_options[j].val == val) + { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) + { + int opt_defs_index = -1; + for (int i = 0; ; i++) + { + if (opt_defs[i].short_option == 0) + break; + else if (opt_defs[i].short_option == val) + { + opt_defs_index = i; + break; + } + } + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0)); + break; + case required_argument: + if (optarg != NULL) + { + int arg_index; + if (missing_argument) + arg_index = -1; + else + arg_index = parse_start + 1; + + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, arg_index)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, -1)); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, parse_start + 1)); + } + break; + default: + // The options table is messed up. Here we'll just continue + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + break; + } + } + else + { + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + } + } +} diff --git a/lldb/source/Core/Baton.cpp b/lldb/source/Core/Baton.cpp new file mode 100644 index 00000000000..103e2e0ef0c --- /dev/null +++ b/lldb/source/Core/Baton.cpp @@ -0,0 +1,25 @@ +//===-- 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 +{ + s->Printf("baton: %p", m_data); +} diff --git a/lldb/source/Core/Broadcaster.cpp b/lldb/source/Core/Broadcaster.cpp new file mode 100644 index 00000000000..b311a8b3eed --- /dev/null +++ b/lldb/source/Core/Broadcaster.cpp @@ -0,0 +1,219 @@ +//===-- 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 (const char *name) : + m_broadcaster_name (name), + m_broadcaster_listeners (), + m_broadcaster_listeners_mutex (Mutex::eMutexTypeRecursive) +{ + 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()); + + // Scope for "listeners_locker" + { + Mutex::Locker listeners_locker(m_broadcaster_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_broadcaster_listeners.end(); + for (pos = m_broadcaster_listeners.begin(); pos != end; ++pos) + pos->first->BroadcasterWillDestruct (this); + + m_broadcaster_listeners.clear(); + } +} + +const ConstString & +Broadcaster::GetBroadcasterName () +{ + return m_broadcaster_name; +} + +void +Broadcaster::AddInitialEventsToListener (Listener *listener, uint32_t requested_events) +{ + +} + +uint32_t +Broadcaster::AddListener (Listener* listener, uint32_t event_mask) +{ + Mutex::Locker locker(m_broadcaster_listeners_mutex); + collection::iterator pos, end = m_broadcaster_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_broadcaster_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_broadcaster_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_broadcaster_listeners_mutex); + if (m_broadcaster_listeners.empty()) + return false; + + collection::iterator pos, end = m_broadcaster_listeners.end(); + for (pos = m_broadcaster_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_broadcaster_listeners_mutex); + collection::iterator pos, end = m_broadcaster_listeners.end(); + // See if we already have this listener, and if so, update its mask + for (pos = m_broadcaster_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_broadcaster_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(); + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + { + StreamString event_description; + event_sp->Dump (&event_description); + log->Printf ("%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, unique =%i)", + this, + m_broadcaster_name.AsCString(""), + event_description.GetData(), + unique); + } + + Mutex::Locker event_types_locker(m_broadcaster_listeners_mutex); + collection::iterator pos, end = m_broadcaster_listeners.end(); + + + // Iterate through all listener/mask pairs + for (pos = m_broadcaster_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); +} + diff --git a/lldb/source/Core/Communication.cpp b/lldb/source/Core/Communication.cpp new file mode 100644 index 00000000000..3ec3e0f41d9 --- /dev/null +++ b/lldb/source/Core/Communication.cpp @@ -0,0 +1,363 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +Communication::Communication(const char *name) : + Broadcaster (name), + m_connection_ap (), + m_read_thread (NULL), + m_read_thread_enabled (false), + m_bytes(), + m_bytes_mutex (Mutex::eMutexTypeRecursive), + m_callback (NULL), + m_callback_baton (NULL) + +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Communication (name = %s)", + this, name); +} + +//---------------------------------------------------------------------- +// 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() +{ + StopReadThread (NULL); + Disconnect (NULL); +} + +ConnectionStatus +Communication::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + + if (m_connection_ap.get()) + return m_connection_ap->BytesAvailable (timeout_usec, error_ptr); + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +Communication::Connect (const char *url, Error *error_ptr) +{ + Clear(); + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Connect (url = %s)", this, url); + + if (m_connection_ap.get()) + return m_connection_ap->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); + + if (m_connection_ap.get()) + { + ConnectionStatus status = m_connection_ap->Disconnect (error_ptr); + m_connection_ap.reset(); + return status; + } + return eConnectionStatusNoConnection; +} + +bool +Communication::IsConnected () const +{ + if (m_connection_ap.get()) + return m_connection_ap->IsConnected (); + return false; +} + +bool +Communication::HasConnection () const +{ + return m_connection_ap.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::Write (dst = %p, dst_len = %zu, timeout_usec = %u) connection = %p", + this, dst, dst_len, timeout_usec, m_connection_ap.get()); + + if (m_read_thread != NULL) + { + // 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_ap.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. + if (m_connection_ap.get()) + { + status = m_connection_ap->BytesAvailable (timeout_usec, error_ptr); + if (status == eConnectionStatusSuccess) + return m_connection_ap->Read (dst, dst_len, 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_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Write (src = %p, src_len = %zu) connection = %p", + this, src, src_len, m_connection_ap.get()); + + if (m_connection_ap.get()) + return m_connection_ap->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 (m_read_thread != LLDB_INVALID_HOST_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); + return m_read_thread != LLDB_INVALID_HOST_THREAD; +} + +bool +Communication::StopReadThread (Error *error_ptr) +{ + if (m_read_thread == NULL) + 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); + + return Host::ThreadJoin (m_read_thread, NULL, error_ptr); +} + + +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.data(), 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) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::AppendBytesToCache (src = %p, src_len = %zu, broadcast = %i)", + this, bytes, len, broadcast); + if (bytes == NULL || len == 0) + 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 + { + 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, ConnectionStatus &status, Error *error_ptr) +{ + if (m_connection_ap.get()) + return m_connection_ap->Read (dst, dst_len, status, error_ptr); + return 0; +} + + +bool +Communication::ReadThreadIsRunning () +{ + return m_read_thread != NULL; +} + +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) + { + status = comm->BytesAvailable (UINT32_MAX, &error); + + if (status == eConnectionStatusSuccess) + { + size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), status, &error); + if (bytes_read > 0) + comm->AppendBytesToCache (buf, bytes_read, true); + } + + switch (status) + { + case eConnectionStatusSuccess: + break; + + case eConnectionStatusNoConnection: // No connection + case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + done = true; + // Fall through... + default: + case eConnectionStatusError: // Check GetError() for details + case eConnectionStatusTimedOut: // Request timed out + error.LogIfError(log, "%p Communication::BytesAvailable () => status = %i", p, status); + break; + } + } + if (log) + log->Printf ("%p Communication::ReadThread () thread exiting...", p); + + // Let clients know that this thread is exiting + comm->m_read_thread = LLDB_INVALID_HOST_THREAD; + 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) +{ + StopReadThread(NULL); + Disconnect (NULL); + m_connection_ap.reset(connection); +} diff --git a/lldb/source/Core/Connection.cpp b/lldb/source/Core/Connection.cpp new file mode 100644 index 00000000000..3c9bb8b1b7e --- /dev/null +++ b/lldb/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/lldb/source/Core/ConnectionFileDescriptor.cpp b/lldb/source/Core/ConnectionFileDescriptor.cpp new file mode 100644 index 00000000000..b22f5ffa36d --- /dev/null +++ b/lldb/source/Core/ConnectionFileDescriptor.cpp @@ -0,0 +1,563 @@ +//===-- ConnectionFileDescriptor.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/ConnectionFileDescriptor.h" + +// C Includes +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#include <sys/types.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/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; + +ConnectionFileDescriptor::ConnectionFileDescriptor () : + Connection(), + m_fd (-1), + m_should_close_fd (false) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, + "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", + this); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : + Connection(), + m_fd (fd), + m_should_close_fd (owns_fd) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, + "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", + this, fd, owns_fd); +} + + +ConnectionFileDescriptor::~ConnectionFileDescriptor () +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, + "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", + this); + Disconnect (NULL); +} + +bool +ConnectionFileDescriptor::IsConnected () const +{ + return m_fd >= 0; +} + +ConnectionStatus +ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::Connect (url = '%s')", + this, s); + + 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, "connect://")) + { + return SocketConnect (s + strlen("connect://"), error_ptr); + } + else if (strstr(s, "file://")) + { + // file:///PATH + const char *path = s + strlen("file://"); + m_fd = ::open (path, O_RDWR); + if (m_fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + int flags = ::fcntl (m_fd, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl (m_fd, F_SETFL, flags); + } + } + m_should_close_fd = true; + return eConnectionStatusSuccess; + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("Unsupported connection URL: '%s'.\n", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("NULL connection URL."); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect (Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::Disconnect ()", + this); + if (m_should_close_fd == false) + { + m_fd = -1; + return eConnectionStatusSuccess; + } + return Close (m_fd, error_ptr); +} + +size_t +ConnectionFileDescriptor::Read (void *dst, size_t dst_len, 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 = %zu)...", + this, m_fd, dst, dst_len); + + Error error; + ssize_t bytes_read = ::read (m_fd, dst, dst_len); + if (bytes_read == 0) + { + error.SetErrorStringWithFormat("End-of-file.\n"); + status = eConnectionStatusLostConnection; + } + else if (bytes_read < 0) + { + error.SetErrorToErrno(); + } + else + { + error.Clear(); + } + + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %zu) => %zi, error = %s", + this, + m_fd, + dst, + dst_len, + 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. + status = eConnectionStatusSuccess; + return 0; + + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + 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 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; + } + +// if (log) +// error->Log(log, "::read ( %i, %p, %zu ) => %i", m_fd, dst, dst_len, bytesread); + Close (m_fd, NULL); + 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 = %zu)", this, src, src_len); + + if (!IsConnected ()) + { + if (error_ptr) + error_ptr->SetErrorString("Not connected."); + status = eConnectionStatusNoConnection; + return 0; + } + + + Error error; + + ssize_t bytes_sent = 0; + + if (m_is_socket) + bytes_sent = ::send (m_fd, src, src_len, 0); + else + bytes_sent = ::write (m_fd, src, src_len); + + if (bytes_sent < 0) + error.SetErrorToErrno (); + else + error.Clear (); + + if (log) + { + if (m_is_socket) + log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %zu, flags = 0) => %zi (error = %s)", + this, m_fd, src, src_len, bytes_sent, error.AsCString()); + else + log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %zu) => %zi (error = %s)", + this, m_fd, src, src_len, bytes_sent, error.AsCString()); + } + + 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.... + } + + Close (m_fd, NULL); + return 0; + } + + status = eConnectionStatusSuccess; + return bytes_sent; +} + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + 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; + } + + while (IsConnected()) + { + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (m_fd, &read_fds); + int nfds = m_fd + 1; + + Error error; + + + if (log) + log->Printf("%p ConnectionFileDescriptor::Write() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p)...", + this, nfds, m_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) + log->Printf("%p ConnectionFileDescriptor::Write() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p) => %d, error = %s", + this, nfds, m_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. + 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) + { + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorString("Not connected."); + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + bool success = true; + if (fd >= 0) + { + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::Close (fd = %i)", + this, + fd); + + success = ::close (fd) == 0; + 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(); + } + fd = -1; + } + m_is_socket = false; + if (success) + return eConnectionStatusSuccess; + else + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::SocketListen (port = %i)", + this, listen_port_num); + + Close (m_fd, false); + m_is_socket = true; + 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); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof sa); + sa.sin_len = sizeof sa; + sa.sin_family = AF_INET; + sa.sin_port = htons (listen_port_num); + sa.sin_addr.s_addr = htonl (INADDR_ANY); + + int err = ::bind (listen_port, (struct sockaddr *) &sa, sizeof(sa)); + 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 = ::accept (listen_port, NULL, 0); + if (m_fd == -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, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketConnect (const char *host_and_port, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, + "%p ConnectionFileDescriptor::SocketConnect (host/port = %s)", + this, host_and_port); + Close (m_fd, false); + m_is_socket = true; + + RegularExpression regex ("([^:]+):([0-9]+)"); + if (regex.Execute (host_and_port, 2) == false) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid host:port specification: '%s'.\n", host_and_port); + return eConnectionStatusError; + } + std::string host_str; + std::string port_str; + if (regex.GetMatchAtIndex (host_and_port, 1, host_str) == false || + regex.GetMatchAtIndex (host_and_port, 2, port_str) == false) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid host:port specification '%s'.\n", host_and_port); + return eConnectionStatusError; + } + + int32_t port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN); + if (port == INT32_MIN) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("Invalid port '%s'.\n", port_str.c_str()); + return eConnectionStatusError; + } + // Create the socket + m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + m_should_close_fd = true; + + // Enable local address reuse + SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::bzero (&sa, sizeof (sa)); + sa.sin_len = 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'.\n", host_str.c_str()); + } + Close (m_fd, false); + return eConnectionStatusError; + } + } + + if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa))) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (m_fd, false); + return eConnectionStatusError; + } + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +int +ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) +{ + return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value)); +} + + diff --git a/lldb/source/Core/ConstString.cpp b/lldb/source/Core/ConstString.cpp new file mode 100644 index 00000000000..c2d09c76461 --- /dev/null +++ b/lldb/source/Core/ConstString.cpp @@ -0,0 +1,480 @@ +//===-- 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; + + +//---------------------------------------------------------------------- +// The global string pool is implemented as a hash_map that maps +// std::string objects to a uint32_t reference count. +// +// In debug builds the value that is stored in the ConstString objects is +// a C string that is owned by one of the std::string objects in the +// hash map. This was done for visibility purposes when debugging as +// gcc was often generating insufficient debug info for the +// iterator objects. +// +// In release builds, the value that is stored in the ConstString objects +// is the iterator into the ConstString::HashMap. This is much faster when +// it comes to modifying the reference count, and removing strings from +// the pool. +//---------------------------------------------------------------------- +class Pool +{ +public: + //------------------------------------------------------------------ + // Default constructor + // + // Initialize the member variables and create the empty string. + //------------------------------------------------------------------ + Pool () : + m_mutex (Mutex::eMutexTypeRecursive), + m_string_map () + { + } + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~Pool () + { + } + + + static llvm::StringMapEntry<uint32_t> & + GetStringMapEntryFromKeyData (const char *keyData) + { + char *ptr = const_cast<char*>(keyData) - sizeof (llvm::StringMapEntry<uint32_t>); + return *reinterpret_cast<llvm::StringMapEntry<uint32_t>*>(ptr); + } + + size_t + GetConstCStringLength (const char *ccstr) + { + if (ccstr) + { + llvm::StringMapEntry<uint32_t>&entry = GetStringMapEntryFromKeyData (ccstr); + return entry.getKey().size(); + } + return 0; + } + + const char * + GetConstCString (const char *cstr) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr); + llvm::StringMapEntry<uint32_t>& entry = m_string_map.GetOrCreateValue (string_ref); + const char *ccstr = entry.getKeyData(); + llvm::StringMapEntry<uint32_t>&reconstituted_entry = GetStringMapEntryFromKeyData (ccstr); + assert (&entry == &reconstituted_entry); + return ccstr; + } + return NULL; + } + + const char * + GetConstCStringWithLength (const char *cstr, int cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr, cstr_len); + llvm::StringMapEntry<uint32_t>& entry = m_string_map.GetOrCreateValue (string_ref); + const char *ccstr = entry.getKeyData(); + llvm::StringMapEntry<uint32_t>&reconstituted_entry = GetStringMapEntryFromKeyData (ccstr); + assert (&entry == &reconstituted_entry); + return ccstr; + } + return NULL; + } + + const char * + GetConstTrimmedCStringWithLength (const char *cstr, int cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + int actual_cstr_len = strlen (cstr); + llvm::StringRef string_ref (cstr, std::min<int>(actual_cstr_len, cstr_len)); + llvm::StringMapEntry<uint32_t>& entry = m_string_map.GetOrCreateValue (string_ref); + const char *ccstr = entry.getKeyData(); + llvm::StringMapEntry<uint32_t>&reconstituted_entry = GetStringMapEntryFromKeyData (ccstr); + assert (&entry == &reconstituted_entry); + return ccstr; + } + return NULL; + } + + //------------------------------------------------------------------ + // Return the size in bytes that this object and any items in its + // collection of uniqued strings + reference 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(llvm::StringMapEntry<uint32_t>) + pos->getKey().size(); + } + return mem_size; + } + +protected: + //------------------------------------------------------------------ + // Typedefs + //------------------------------------------------------------------ + typedef llvm::StringMap<uint32_t, llvm::BumpPtrAllocator> StringPool; + 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. +//---------------------------------------------------------------------- +static Pool & +StringPool() +{ + static Pool string_pool; + return string_pool; +} + +//---------------------------------------------------------------------- +// Default constructor +// +// Initializes the string to an empty string. +//---------------------------------------------------------------------- +ConstString::ConstString () : + m_string (NULL) +{ +} + +//---------------------------------------------------------------------- +// Copy constructor +// +// Copies the string value in "rhs" and retains an extra reference +// to the string value in the string pool. +//---------------------------------------------------------------------- +ConstString::ConstString (const ConstString& rhs) : + m_string (rhs.m_string) +{ +} + +//---------------------------------------------------------------------- +// Construct with C String value +// +// Constructs this object with a C string by looking to see if the +// C string already exists in the global string pool. If it does +// exist, it retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +ConstString::ConstString (const char *cstr) : + m_string (StringPool().GetConstCString (cstr)) +{ +} + +//---------------------------------------------------------------------- +// Construct with C String value with max length +// +// Constructs this object with a C string with a length. If +// the length of the string is greather than "cstr_len", the +// string length will be truncated. This allows substrings to be +// created without the need to NULL terminate the string as it +// is passed into this function. +// +// If the C string already exists in the global string pool, it +// retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +ConstString::ConstString (const char *cstr, size_t cstr_len) : + m_string (StringPool().GetConstCStringWithLength (cstr, cstr_len)) +{ +} + +//---------------------------------------------------------------------- +// Destructor +// +// Decrements the reference count on the contained string, and if +// the resulting reference count is zero, then the string is removed +// from the string pool. If the reference count is still greater +// than zero, the string will remain in the string pool +//---------------------------------------------------------------------- +ConstString::~ConstString () +{ +} + +//---------------------------------------------------------------------- +// Convert to pointer operator. This allows code to check any +// ConstString objects to see if they contain anything (not empty) +// valid using code such as: +// +// ConstString str(...); +// if (str) +// { ... +//---------------------------------------------------------------------- +ConstString::operator void*() const +{ + return IsEmpty() ? NULL : const_cast<ConstString*>(this); +} + +//---------------------------------------------------------------------- +// Assignment operator +// +// Assigns the string in this object with the value from "rhs" +// and increments the reference count of that string. +// +// The previously contained string will be get its reference count +// decremented and removed from the string pool if its reference +// count reaches zero. +//---------------------------------------------------------------------- +const ConstString& +ConstString::operator=(const ConstString& rhs) +{ + m_string = rhs.m_string; + return *this; +} + +//---------------------------------------------------------------------- +// Equal to operator +// +// Returns true if this string is equal to that in "rhs". This is +// very fast as it results in a pointer comparison since all strings +// are in a uniqued and reference counted string pool. +//------------------------------------------------------------------ +bool +ConstString::operator == (const ConstString& rhs) const +{ + // We can do a pointer compare to compare these strings since they + // must come from the same pool in order to be equal. + return m_string == rhs.m_string; +} + +bool +ConstString::operator != (const ConstString& rhs) const +{ + return m_string != rhs.m_string; +} + +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 the string value "str" to the stream "s" +//---------------------------------------------------------------------- +Stream& +lldb_private::operator << (Stream& s, const ConstString& str) +{ + const char *cstr = str.GetCString(); + if (cstr) + s << cstr; + + return s; +} + +//---------------------------------------------------------------------- +// Get the value of the contained string as a NULL terminated C +// string value. Return "fail_value" if the string is empty. +//---------------------------------------------------------------------- +const char * +ConstString::AsCString(const char *fail_value) const +{ + if (m_string == NULL) + return fail_value; + return m_string; +} + +const char * +ConstString::GetCString () const +{ + return m_string; +} + +size_t +ConstString::GetLength () const +{ + return StringPool().GetConstCStringLength (m_string); +} + + +//---------------------------------------------------------------------- +// Clear any contained string and reset the value to the an empty +// string value. +// +// The previously contained string will be get its reference count +// decremented and removed from the string pool if its reference +// count reaches zero. +//---------------------------------------------------------------------- +void +ConstString::Clear () +{ + m_string = NULL; +} + +//---------------------------------------------------------------------- +// Compare two string objects. +// +// Returns: +// -1 if a < b +// 0 if a == b +// 1 if a > b +//---------------------------------------------------------------------- +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 +} + +//---------------------------------------------------------------------- +// Dump the string value to the stream "s". If the contained string +// is empty, print "fail_value" to the stream instead. If +// "fail_value" is NULL, then nothing will be dumped to the +// stream. +//---------------------------------------------------------------------- +void +ConstString::Dump(Stream *s, const char *fail_value) const +{ + const char *cstr = AsCString (fail_value); + if (cstr) + s->PutCString (cstr); +} + +//---------------------------------------------------------------------- +// Dump extra debug information to the stream "s". +//---------------------------------------------------------------------- +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 = %zu", (int)sizeof(void*) * 2, this, parens, cstr, parens, cstr_len); +} + +//---------------------------------------------------------------------- +// Returns true if the contained string is empty. +//---------------------------------------------------------------------- +bool +ConstString::IsEmpty() const +{ + return m_string == NULL || m_string[0] == '\0'; +} + +//---------------------------------------------------------------------- +// Set the string value in the object by uniquing the "cstr" string +// value in our global string pool. +// +// If the C string already exists in the global string pool, it +// retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +void +ConstString::SetCString (const char *cstr) +{ + m_string = StringPool().GetConstCString (cstr); +} + +//---------------------------------------------------------------------- +// Set the string value in the object by uniquing "cstr_len" bytes +// starting at the "cstr" string value in our global string pool. +// If trim is true, then "cstr_len" indicates a maximum length of +// the CString and if the actual length of the string is less, then +// it will be trimmed. If trim is false, then this allows strings +// with NULL characters ('\0') to be added to the string pool. +// +// If the C string already exists in the global string pool, it +// retains an extra reference to the string in the string +// pool. If it doesn't exist, it is added to the string pool with +// a reference count of 1. +//---------------------------------------------------------------------- +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); +} + +//---------------------------------------------------------------------- +// Return the size in bytes that this object takes in memory. The +// resulting size will not include any of the C string values from +// the global string pool (see StaticMemorySize ()). +//---------------------------------------------------------------------- +size_t +ConstString::MemorySize() const +{ + return sizeof(ConstString); +} + +//---------------------------------------------------------------------- +// Reports the the size in bytes of all shared C string values, +// containers and reference count values as a byte size for the +// entire string pool. +//---------------------------------------------------------------------- +size_t +ConstString::StaticMemorySize() +{ + // Get the size of the static string pool + return StringPool().MemorySize(); +} diff --git a/lldb/source/Core/DataBufferHeap.cpp b/lldb/source/Core/DataBufferHeap.cpp new file mode 100644 index 00000000000..a93427ff858 --- /dev/null +++ b/lldb/source/Core/DataBufferHeap.cpp @@ -0,0 +1,105 @@ +//===-- 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 (size_t n, uint8_t ch) : + m_data(n, ch) +{ +} + +//---------------------------------------------------------------------- +// Initialize this class with a copy of the "n" bytes from the "bytes" +// buffer. +//---------------------------------------------------------------------- +DataBufferHeap::DataBufferHeap (const void *src, size_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. +//---------------------------------------------------------------------- +size_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. +//---------------------------------------------------------------------- +size_t +DataBufferHeap::SetByteSize (size_t new_size) +{ + m_data.resize(new_size); + return m_data.size(); +} + +void +DataBufferHeap::CopyData (const void *src, size_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(); +} + + + diff --git a/lldb/source/Core/DataBufferMemoryMap.cpp b/lldb/source/Core/DataBufferMemoryMap.cpp new file mode 100644 index 00000000000..c8ad5492f51 --- /dev/null +++ b/lldb/source/Core/DataBufferMemoryMap.cpp @@ -0,0 +1,214 @@ +//===-- 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 <sys/stat.h> +#include <sys/mman.h> + +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Host/Host.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +DataBufferMemoryMap::DataBufferMemoryMap() : + m_mmap_addr(NULL), + m_mmap_size(0), + m_data(NULL), + m_size(0), + m_error() +{ +} + +//---------------------------------------------------------------------- +// 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. +//---------------------------------------------------------------------- +size_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) + { + ::munmap((void *)m_mmap_addr, m_mmap_size); + m_mmap_addr = NULL; + m_mmap_size = 0; + m_data = NULL; + m_size = 0; + } + m_error.Clear(); +} + + +const Error & +DataBufferMemoryMap::GetError() const +{ + return m_error; +} + +//---------------------------------------------------------------------- +// Memory map "length" bytes from "file" starting "offset" +// bytes into the file. If "length" is set to SIZE_T_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* file, off_t offset, size_t length) +{ + if (file != NULL) + { + char path[PATH_MAX]; + if (file->GetPath(path, sizeof(path))) + { + int fd = ::open(path, O_RDONLY, 0); + if (fd >= 0) + { + MemoryMapFromFileDescriptor (fd, offset, length); + ::close(fd); + return GetByteSize(); + } + else + { + m_error.SetErrorToErrno(); + return 0; + } + } + } + // 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_T_MAX, then map as many bytes +// as possible. +// +// RETURNS +// Number of bytes mapped starting from the requested offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, off_t offset, size_t length) +{ + Clear(); + if (fd >= 0) + { + struct stat stat; + if (::fstat(fd, &stat) == 0) + { + if ((stat.st_mode & S_IFREG) && (stat.st_size > offset)) + { + if (length == SIZE_T_MAX) + length = stat.st_size - offset; + + // Cap the length if too much data was requested + if (length > stat.st_size - offset) + length = stat.st_size - offset; + + if (length > 0) + { + m_mmap_addr = (uint8_t *)::mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, offset); + + if (m_mmap_addr == (void*)-1) + { + m_error.SetErrorToErrno (); + if (m_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_READ, MAP_FILE | MAP_SHARED, 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 + m_error.Clear (); + + // 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; + } + } + } + } + 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; + } + } + } + } + + ::close (fd); + } + return GetByteSize (); +} diff --git a/lldb/source/Core/DataExtractor.cpp b/lldb/source/Core/DataExtractor.cpp new file mode 100644 index 00000000000..236ec187f5f --- /dev/null +++ b/lldb/source/Core/DataExtractor.cpp @@ -0,0 +1,1517 @@ +//===-- 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 <libkern/OSByteOrder.h> +#include <stddef.h> + +#include <bitset> +#include <string> + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBuffer.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" + +using namespace lldb; +using namespace lldb_private; + +#define NON_PRINTABLE_CHAR '.' +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor () : + m_start (NULL), + m_end (NULL), + m_byte_order(eByteOrderHost), + 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, 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 DataExtractor objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (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); +} + +//---------------------------------------------------------------------- +// 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, uint32_t offset, uint32_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)) + { + uint32_t bytes_available = data.GetByteSize() - offset; + if (length > bytes_available) + length = bytes_available; + SetData(data, offset, length); + } +} + +//---------------------------------------------------------------------- +// 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 = eByteOrderHost; + m_addr_size = 4; + m_data_sp.reset(); +} + +//------------------------------------------------------------------ +// Returns the total number of bytes that this object refers to +//------------------------------------------------------------------ +size_t +DataExtractor::GetByteSize () const +{ + return m_end - m_start; +} + +//------------------------------------------------------------------ +// 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; +} + +//------------------------------------------------------------------ +// Returns true if OFFSET is a valid offset into the data in this +// object. +//------------------------------------------------------------------ +bool +DataExtractor::ValidOffset (uint32_t offset) const +{ + return offset < GetByteSize(); +} + +//------------------------------------------------------------------ +// Returns true if there are LENGTH bytes availabe starting OFFSET +// into the data that is in this object. +//------------------------------------------------------------------ +bool +DataExtractor::ValidOffsetForDataOfSize (uint32_t offset, uint32_t length) const +{ + size_t size = GetByteSize(); + if (offset >= size) + return false; // offset isn't valid + + if (length == 0) + return true; // No bytes requested at this offset, return true + + // If we flip the bits in offset we can figure out how + // many bytes we have left before "offset + length" + // could overflow when doing unsigned arithmetic. + if (length > ~offset) + return false; // unsigned overflow + + // Make sure "offset + length" is a valid offset as well. + // length must be greater than zero for this to be a + // valid expression, and we have already checked for this. + return ((offset + length) <= size); +} + +//------------------------------------------------------------------ +// Returns a pointer to the first byte contained in this object's +// data, or NULL of there is no data in this object. +//------------------------------------------------------------------ +const uint8_t * +DataExtractor::GetDataStart () const +{ + return m_start; +} +//------------------------------------------------------------------ +// Returns a pointer to the byte past the last byte contained in +// this object's data, or NULL of there is no data in this object. +//------------------------------------------------------------------ +const uint8_t * +DataExtractor::GetDataEnd () const +{ + return m_end; +} + +//------------------------------------------------------------------ +// Returns true if this object will endian swap values as it +// extracts data. +//------------------------------------------------------------------ +ByteOrder +DataExtractor::GetByteOrder () const +{ + return m_byte_order; +} +//------------------------------------------------------------------ +// Set wether this object will endian swap values as it extracts +// data. +//------------------------------------------------------------------ +void +DataExtractor::SetByteOrder (ByteOrder endian) +{ + m_byte_order = endian; +} + + +//------------------------------------------------------------------ +// Return the size in bytes of any address values this object will +// extract +//------------------------------------------------------------------ +uint8_t +DataExtractor::GetAddressByteSize () const +{ + return m_addr_size; +} + +//------------------------------------------------------------------ +// Set the size in bytes that will be used when extracting any +// address values from data contained in this object. +//------------------------------------------------------------------ +void +DataExtractor::SetAddressByteSize (uint8_t addr_size) +{ + m_addr_size = addr_size; +} + +//---------------------------------------------------------------------- +// 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 +DataExtractor::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 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". +//---------------------------------------------------------------------- +uint32_t +DataExtractor::SetData (const DataExtractor& data, uint32_t data_offset, uint32_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. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::SetData (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. +//---------------------------------------------------------------------- +uint8_t +DataExtractor::GetU8 (uint32_t *offset_ptr) const +{ + uint8_t val = 0; + if ( m_start < m_end ) + { + val = m_start[*offset_ptr]; + *offset_ptr += sizeof(val); + } + return val; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr, void *dst, uint32_t count) const +{ + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, count) ) + { + // Copy the data into the buffer + memcpy (dst, m_start + offset, count); + // Advance the offset + *offset_ptr += 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 (uint32_t *offset_ptr) const +{ + uint16_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt16(m_start, offset); + else + val = _OSReadInt16 (m_start, offset); + + // Advance the offset + *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 (uint32_t *offset_ptr, void *void_dst, uint32_t count) const +{ + uint16_t *dst = (uint16_t *)void_dst; + const size_t value_size = sizeof(*dst); + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, value_size * count) ) + { + uint16_t *value_ptr; + uint16_t *end = dst + count; + if (m_byte_order != eByteOrderHost) + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = OSReadSwapInt16 (m_start, offset); + } + else + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = _OSReadInt16 (m_start, offset); + } + + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of success + return 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 (uint32_t *offset_ptr) const +{ + uint32_t val = 0; + register uint32_t offset = *offset_ptr; + + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt32 (m_start, offset); + else + val = _OSReadInt32 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + 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 (uint32_t *offset_ptr, void *void_dst, uint32_t count) const +{ + uint32_t *dst = (uint32_t *)void_dst; + const size_t value_size = sizeof(*dst); + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, value_size * count)) + { + uint32_t *value_ptr; + uint32_t *end = dst + count; + if (m_byte_order != eByteOrderHost) + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = OSReadSwapInt32 (m_start, offset); + + } + else + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = _OSReadInt32 (m_start, offset); + } + + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of success + return 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 (uint32_t *offset_ptr) const +{ + uint64_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt64 (m_start, offset); + else + val = _OSReadInt64 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + 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 (uint32_t *offset_ptr, void *void_dst, uint32_t count) const +{ + uint64_t *dst = (uint64_t *)void_dst; + const size_t value_size = sizeof(uint64_t); + register uint32_t offset = *offset_ptr; + + if ((count > 0) && ValidOffsetForDataOfSize(offset, value_size * count)) + { + uint64_t *value_ptr; + uint64_t *end = dst + count; + if (m_byte_order != eByteOrderHost) + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = OSReadSwapInt64 (m_start, offset); + + } + else + { + for (value_ptr = dst; value_ptr < end; ++value_ptr, offset += value_size) + *value_ptr = _OSReadInt64 (m_start, offset); + } + + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of success + return 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 (uint32_t *offset_ptr, uint32_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!"); + 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 (uint32_t *offset_ptr, uint32_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!"); + break; + } + return 0; +} + +int64_t +DataExtractor::GetMaxS64 (uint32_t *offset_ptr, uint32_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!"); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64Bitfield (uint32_t *offset_ptr, uint32_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 = ((1 << bitfield_bit_size) - 1); + uval64 &= bitfield_mask; + } + return uval64; +} + +int64_t +DataExtractor::GetMaxS64Bitfield (uint32_t *offset_ptr, uint32_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 = ((1 << bitfield_bit_size) - 1); + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & (1 << (bitfield_bit_size - 1))) + sval64 |= ~bitfield_mask; + } + return sval64; +} + + +float +DataExtractor::GetFloat (uint32_t *offset_ptr) const +{ + uint32_t val = 0; + register uint32_t offset = *offset_ptr; + + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt32 (m_start, offset); + else + val = _OSReadInt32 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return *((float *)&val); +} + +double +DataExtractor::GetDouble (uint32_t *offset_ptr) const +{ + uint64_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt64 (m_start, offset); + else + val = _OSReadInt64 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return *((double *)&val); + +} + + +long double +DataExtractor::GetLongDouble (uint32_t *offset_ptr) const +{ + if (sizeof(long double) == sizeof(uint64_t)) + { + uint64_t val = 0; + register uint32_t offset = *offset_ptr; + if ( ValidOffsetForDataOfSize(offset, sizeof(val)) ) + { + if (m_byte_order != eByteOrderHost) + val = OSReadSwapInt64 (m_start, offset); + else + val = _OSReadInt64 (m_start, offset); + + // Advance the offset + *offset_ptr += sizeof(val); + } + return *((long double *)&val); + } + return 0.0; +} + + +//------------------------------------------------------------------ +// 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 (uint32_t *offset_ptr) const +{ + return GetMaxU64 (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 (uint32_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 (uint32_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_GNU_EH_PE_omit) + return ULONG_LONG_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_GNU_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_GNU_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_GNU_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_GNU_EH_PE_funcrel: + signExtendValue = true; + break; + + case DW_GNU_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_GNU_EH_PE_MASK_ENCODING) + { + case DW_GNU_EH_PE_absptr : + { + addressValue = GetAddress (offset_ptr); +// if (data_relocs) +// addressValue = data_relocs->Relocate(*offset_ptr - addr_size, *this, addressValue); + } + break; + case DW_GNU_EH_PE_uleb128 : addressValue = GetULEB128(offset_ptr); break; + case DW_GNU_EH_PE_udata2 : addressValue = GetU16(offset_ptr); break; + case DW_GNU_EH_PE_udata4 : addressValue = GetU32(offset_ptr); break; + case DW_GNU_EH_PE_udata8 : addressValue = GetU64(offset_ptr); break; + case DW_GNU_EH_PE_sleb128 : addressValue = GetSLEB128(offset_ptr); break; + case DW_GNU_EH_PE_sdata2 : addressValue = (int16_t)GetU16(offset_ptr); break; + case DW_GNU_EH_PE_sdata4 : addressValue = (int32_t)GetU32(offset_ptr); break; + case DW_GNU_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 (uint32_t offset, uint32_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; +} +//---------------------------------------------------------------------- +// Peeks at bytes in the contained data. +// +// Returns a valid pointer to bytes if "offset" is a valid offset in +// and there are "length" bytes available, else NULL is returned. +//---------------------------------------------------------------------- +const uint8_t* +DataExtractor::PeekData (uint32_t offset, uint32_t length) const +{ + if ( length > 0 && ValidOffsetForDataOfSize(offset, length) ) + return m_start + offset; + return NULL; +} + +//---------------------------------------------------------------------- +// Returns a pointer to a bytes in this object's data at the offset +// pointed to by "offset_ptr". If "length" is zero or too large, +// then the offset pointed to by "offset_ptr" will not be updated +// and NULL will be returned. +// +// Returns a pointer to the data if the offset and length are valid, +// or NULL otherwise. +//---------------------------------------------------------------------- +const void* +DataExtractor::GetData (uint32_t *offset_ptr, uint32_t length) const +{ + const uint8_t* bytes = NULL; + register uint32_t offset = *offset_ptr; + if ( length > 0 && ValidOffsetForDataOfSize(offset, length) ) + { + bytes = m_start + offset; + *offset_ptr = offset + length; + } + return bytes; +} + +//---------------------------------------------------------------------- +// Extracts a AsCString (fixed length, or variable length) from +// the data at the offset pointed to by "offset_ptr". If +// "length" is zero, then a variable length NULL terminated C +// string will be extracted from the data the "offset_ptr" will be +// updated with the offset of the byte that follows the NULL +// terminator byte. If "length" is greater than zero, then +// the function will make sure there are "length" bytes +// available in the current data and if so, return a valid pointer. +// +// 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 (uint32_t *offset_ptr) const +{ + const char *s = NULL; + if ( m_start < m_end ) + { + s = (char*)m_start + *offset_ptr; + + size_t length = strlen(s) + 1; + + if (!ValidOffsetForDataOfSize(*offset_ptr, length)) + return NULL; + + // Advance the offset + *offset_ptr += length; + } + return s; +} + +//------------------------------------------------------------------ +// 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 (uint32_t offset) const +{ + if (ValidOffset (offset)) + return (const char*)m_start + offset; + return NULL; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr) const +{ + uint64_t result = 0; + if ( m_start < m_end ) + { + int shift = 0; + const uint8_t *src = m_start + *offset_ptr; + uint8_t byte; + int bytecount = 0; + + while (src < m_end) + { + bytecount++; + byte = *src++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + + *offset_ptr += bytecount; + } + return result; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr) const +{ + int64_t result = 0; + + if ( m_start < m_end ) + { + int shift = 0; + int size = sizeof (uint32_t) * 8; + const uint8_t *src = m_start + *offset_ptr; + + uint8_t byte; + int bytecount = 0; + + while (src < m_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; +} + +//---------------------------------------------------------------------- +// 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 (uint32_t *offset_ptr) const +{ + uint32_t bytes_consumed = 0; + if ( m_start < m_end ) + { + const uint8_t *start = m_start + *offset_ptr; + const uint8_t *src = start; + + while ((src < m_end) && (*src++ & 0x80)) + ++bytes_consumed; + + *offset_ptr += src - start; + } + return bytes_consumed; +} + +uint32_t +DataExtractor::Dump +( + Stream *s, + uint32_t start_offset, + lldb::Format item_format, + uint32_t item_byte_size, + uint32_t item_count, + uint32_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 +) const +{ + if (s == NULL) + return start_offset; + + uint32_t offset; + uint32_t count; + uint32_t line_start_offset; + + if (item_format == eFormatPointer) + { + if (item_byte_size != 4 && item_byte_size != 8) + item_byte_size = s->GetAddressByteSize(); + } + + for (offset = start_offset, line_start_offset = start_offset, 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", (num_per_line - (offset - line_start_offset)) * 3 + 2, ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + } + s->EOL(); + } + if (base_addr != LLDB_INVALID_ADDRESS) + s->Printf ("0x%8.8llx: ", (uint64_t)(base_addr + (offset - start_offset))); + line_start_offset = offset; + } + else + if (item_format != eFormatChar && + item_format != eFormatCharPrintable && + count > 0) + { + s->PutChar(' '); + } + + uint32_t i; + switch (item_format) + { + case eFormatBoolean: + s->Printf ("%s", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset) ? "true" : "false"); + break; + + case eFormatBinary: + { + uint64_t uval64 = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + std::string binary_value(std::bitset<64>(uval64).to_string()); + 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); + } + 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: + { + // If we are only printing one character surround it with single + // quotes + if (item_count == 1 && item_format == eFormatChar) + s->PutChar('\''); + + uint8_t ch = GetU8(&offset); + if (isprint(ch)) + s->Printf ("%c", ch); + else if (item_format == eFormatChar) + { + switch (ch) + { + case '\e': s->Printf ("\\e", (uint8_t)ch); break; + case '\a': s->Printf ("\\a", ch); break; + case '\b': s->Printf ("\\b", ch); break; + case '\f': s->Printf ("\\f", ch); break; + case '\n': s->Printf ("\\n", ch); break; + case '\r': s->Printf ("\\r", ch); break; + case '\t': s->Printf ("\\t", ch); break; + case '\v': s->Printf ("\\v", ch); break; + case '\0': s->Printf ("\\0", ch); break; + default: s->Printf ("\\x%2.2x", 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 eFormatComplex: + if (sizeof(float) * 2 == item_byte_size) + { + uint32_t a32 = GetU32(&offset); + uint32_t b32 = GetU32(&offset); + + s->Printf ("%g + %gi", a32, b32); + } + else if (sizeof(double) * 2 == item_byte_size) + { + uint64_t a64 = GetU64(&offset); + uint64_t b64 = GetU64(&offset); + + s->Printf ("%lg + %lgi", a64, b64); + } + else if (sizeof(long double) * 2 == item_byte_size && sizeof(long double) <= sizeof(uint64_t)) + { + uint64_t a64 = GetU64(&offset); + uint64_t b64 = GetU64(&offset); + s->Printf ("%Lg + %Lgi", a64, b64); + } + break; + + case eFormatDecimal: + if (item_byte_size <= 8); + s->Printf ("%lld", GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatUnsigned: + if (item_byte_size <= 8); + s->Printf ("%llu", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatOctal: + if (item_byte_size <= 8); + s->Printf ("0%llo", GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatEnum: + // Print enum value as a signed integer when we don't get the enum type + s->Printf ("%lld", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + break; + + case eFormatCString: + { + const char *cstr = GetCStr(&offset); + if (cstr) + s->Printf("\"%s\"", cstr); + else + { + s->Printf("NULL", cstr); + offset = UINT32_MAX; + } + } + break; + + + case eFormatPointer: + s->Address(GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset), sizeof (addr_t)); + break; + + case eFormatHex: + if (item_byte_size <= 8) + { + s->Printf("0x%*.*llx", 2 * item_byte_size, 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"); + int32_t start_idx, end_idx, delta; + if (m_byte_order == eByteOrderBig) + { + start_idx = offset; + end_idx = offset + item_byte_size; + delta = 1; + } + else + { + start_idx = offset + item_byte_size - 1; + end_idx = -1; + delta = -1; + } + const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); + if (bytes) + { + for (int32_t idx = start_idx; idx != end_idx; idx += delta) + s->Printf("%2.2x", bytes[idx]); + } + } + break; + + case eFormatFloat: + if (sizeof(float) == item_byte_size) + { + uint32_t a32 = GetU32(&offset); + s->Printf ("%g", (double)(*((float *)&a32))); + } + else if (sizeof(double) == item_byte_size) + { + uint64_t a64 = GetU64(&offset); + s->Printf ("%lg", (*((double *)&a64))); + } + else if (sizeof(long double) == item_byte_size && sizeof(long double) <= sizeof(uint64_t)) + { + uint64_t a64 = GetU64(&offset); + s->Printf ("%Lg", (*((long double *)&a64))); + } + break; + + case eFormatUnicode16: + s->Printf("0x%4.4x", GetU16 (&offset)); + break; + + case eFormatUnicode32: + s->Printf("0x%8.8x", GetU32 (&offset)); + break; + + case eFormatVectorOfChar: + s->PutChar('{'); + offset = Dump (s, start_offset, eFormatChar, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt8: + s->PutChar('{'); + offset = Dump (s, start_offset, eFormatDecimal, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt8: + s->PutChar('{'); + offset = Dump (s, start_offset, eFormatHex, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt16: + s->PutChar('{'); + offset = Dump (s, start_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, start_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, start_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, start_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, start_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, start_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 eFormatVectorOfFloat32: + s->PutChar('{'); + offset = Dump (s, start_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, start_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, start_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", (num_per_line - (offset - line_start_offset)) * 3 + 2, ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, UINT32_MAX, 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(). +//---------------------------------------------------------------------- +uint32_t +DataExtractor::PutToLog +( + Log *log, + uint32_t start_offset, + uint32_t length, + uint64_t base_addr, + uint32_t num_per_line, + DataExtractor::Type type, + const char *format +) const +{ + if (log == NULL) + return start_offset; + + uint32_t offset; + uint32_t end_offset = offset + length; + uint32_t count; + StreamString sstr; + for (offset = start_offset, 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.8llx:", (uint64_t)(base_addr + (offset - start_offset))); + } + + switch (type) + { + default: + 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.16llx", GetU64(&offset)); break; + case TypePointer: sstr.Printf (format ? format : " 0x%llx", GetAddress(&offset)); break; + case TypeULEB128: sstr.Printf (format ? format : " 0x%llx", GetULEB128(&offset)); break; + case TypeSLEB128: sstr.Printf (format ? format : " %lld", 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, uint32_t offset) const +{ + if (s) + { + const uint8_t *uuid_data = PeekData(offset, 16); + if ( uuid_data ) + { + UUID uuid(uuid_data, 16); + uuid.Dump(s); + } + else + { + s->Printf("<not enough data for UUID at offset 0x%8.8x>", offset); + } + } +} + + diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp new file mode 100644 index 00000000000..c106d838fed --- /dev/null +++ b/lldb/source/Core/Debugger.cpp @@ -0,0 +1,434 @@ +//===-- 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-private.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Target/TargetList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + + +using namespace lldb; +using namespace lldb_private; + +int Debugger::g_shared_debugger_refcount = 0; +bool Debugger::g_in_terminate = false; + +Debugger::DebuggerSP & +Debugger::GetDebuggerSP () +{ + static DebuggerSP g_shared_debugger_sp; + return g_shared_debugger_sp; +} + +void +Debugger::Initialize () +{ + g_shared_debugger_refcount++; + if (GetDebuggerSP().get() == NULL) + { + GetDebuggerSP().reset (new Debugger()); + lldb_private::Initialize(); + GetDebuggerSP()->GetCommandInterpreter().Initialize(); + } +} + +void +Debugger::Terminate () +{ + g_shared_debugger_refcount--; + if (g_shared_debugger_refcount == 0) + { + // Because Terminate is called also in the destructor, we need to make sure + // that none of the calls to GetSharedInstance leads to a call to Initialize, + // thus bumping the refcount back to 1 & causing Debugger::~Debugger to try to + // re-terminate. So we use g_in_terminate to indicate this condition. + // When we can require at least Initialize to be called, we won't have to do + // this since then the GetSharedInstance won't have to auto-call Initialize... + + g_in_terminate = true; + int num_targets = GetDebuggerSP()->GetTargetList().GetNumTargets(); + for (int i = 0; i < num_targets; i++) + { + ProcessSP process_sp(GetDebuggerSP()->GetTargetList().GetTargetAtIndex (i)->GetProcessSP()); + if (process_sp) + process_sp->Destroy(); + } + GetDebuggerSP()->DisconnectInput(); + lldb_private::WillTerminate(); + GetDebuggerSP().reset(); + } +} + +Debugger & +Debugger::GetSharedInstance() +{ + // Don't worry about thread race conditions with the code below as + // lldb_private::Initialize(); does this in a thread safe way. I just + // want to avoid having to lock and unlock a mutex in + // lldb_private::Initialize(); every time we want to access the + // Debugger shared instance. + + // FIXME: We intend to require clients to call Initialize by hand (since they + // will also have to call Terminate by hand.) But for now it is not clear where + // we can reliably call these in JH. So the present version initializes on first use + // here, and terminates in the destructor. + if (g_shared_debugger_refcount == 0 && !g_in_terminate) + Initialize(); + + assert(GetDebuggerSP().get()!= NULL); + return *(GetDebuggerSP().get()); +} + +Debugger::Debugger () : + m_input_comm("debugger.input"), + m_input_file (), + m_output_file (), + m_error_file (), + m_async_execution (true), + m_target_list (), + m_listener ("lldb.Debugger"), + m_source_manager (), + m_command_interpreter (eScriptLanguageDefault, false, &m_listener, m_source_manager), + m_input_readers (), + m_input_reader_data () +{ +} + +Debugger::~Debugger () +{ + // FIXME: + // Remove this once this version of lldb has made its way through a build. + Terminate(); +} + + +bool +Debugger::GetAsyncExecution () +{ + return m_async_execution; +} + +void +Debugger::SetAsyncExecution (bool async_execution) +{ + static bool value_has_been_set = false; + + if (!value_has_been_set) + { + value_has_been_set = true; + m_async_execution = async_execution; + m_command_interpreter.SetSynchronous (!async_execution); + } +} + +void +Debugger::DisconnectInput() +{ + m_input_comm.Clear (); +} + +void +Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_input_file.SetFileHandle (fh, tranfer_ownership); + if (m_input_file.GetFileHandle() == NULL) + m_input_file.SetFileHandle (stdin, false); + + // Disconnect from any old connection if we had one + m_input_comm.Disconnect (); + m_input_comm.SetConnection (new ConnectionFileDescriptor (::fileno (GetInputFileHandle()), true)); + m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); + + Error error; + if (m_input_comm.StartReadThread (&error) == false) + { + FILE *err_fh = GetErrorFileHandle(); + if (err_fh) + { + ::fprintf (err_fh, "error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); + exit(1); + } + } + +} + +FILE * +Debugger::GetInputFileHandle () +{ + return m_input_file.GetFileHandle(); +} + + +void +Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_output_file.SetFileHandle (fh, tranfer_ownership); + if (m_output_file.GetFileHandle() == NULL) + m_output_file.SetFileHandle (stdin, false); +} + +FILE * +Debugger::GetOutputFileHandle () +{ + return m_output_file.GetFileHandle(); +} + +void +Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) +{ + m_error_file.SetFileHandle (fh, tranfer_ownership); + if (m_error_file.GetFileHandle() == NULL) + m_error_file.SetFileHandle (stdin, false); +} + + +FILE * +Debugger::GetErrorFileHandle () +{ + return m_error_file.GetFileHandle(); +} + +CommandInterpreter & +Debugger::GetCommandInterpreter () +{ + return m_command_interpreter; +} + +Listener & +Debugger::GetListener () +{ + return m_listener; +} + + +TargetSP +Debugger::GetCurrentTarget () +{ + return m_target_list.GetCurrentTarget (); +} + +ExecutionContext +Debugger::GetCurrentExecutionContext () +{ + ExecutionContext exe_ctx; + exe_ctx.Clear(); + + lldb::TargetSP target_sp = GetCurrentTarget(); + exe_ctx.target = target_sp.get(); + + if (target_sp) + { + exe_ctx.process = target_sp->GetProcessSP().get(); + if (exe_ctx.process && exe_ctx.process->IsRunning() == false) + { + exe_ctx.thread = exe_ctx.process->GetThreadList().GetCurrentThread().get(); + if (exe_ctx.thread == NULL) + exe_ctx.thread = exe_ctx.process->GetThreadList().GetThreadAtIndex(0).get(); + if (exe_ctx.thread) + { + exe_ctx.frame = exe_ctx.thread->GetCurrentFrame().get(); + if (exe_ctx.frame == NULL) + exe_ctx.frame = exe_ctx.thread->GetStackFrameAtIndex (0).get(); + } + } + } + return exe_ctx; + +} + +SourceManager & +Debugger::GetSourceManager () +{ + return m_source_manager; +} + + +TargetList& +Debugger::GetTargetList () +{ + return m_target_list; +} + +void +Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) +{ + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); +} + + +void +Debugger::DispatchInput (const char *bytes, size_t bytes_len) +{ + if (bytes == NULL || bytes_len == 0) + return; + + // TODO: implement the STDIO to the process as an input reader... + TargetSP target = GetCurrentTarget(); + if (target.get() != NULL) + { + ProcessSP process_sp = target->GetProcessSP(); + if (process_sp.get() != NULL + && StateIsRunningState (process_sp->GetState())) + { + Error error; + if (process_sp->PutSTDIN (bytes, bytes_len, error) == bytes_len) + return; + } + } + + WriteToDefaultReader (bytes, bytes_len); +} + +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_readers.empty() && !m_input_reader_data.empty()) + { + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + + // Get the input reader from the top of the stack + InputReaderSP reader_sp(m_input_readers.top()); + + if (!reader_sp) + break; + + size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.data(), + 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 donesvn + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + +} + +void +Debugger::PushInputReader (const InputReaderSP& reader_sp) +{ + if (!reader_sp) + return; + if (!m_input_readers.empty()) + { + // Deactivate the old top reader + InputReaderSP top_reader_sp (m_input_readers.top()); + if (top_reader_sp) + top_reader_sp->Notify (eInputReaderDeactivate); + } + m_input_readers.push (reader_sp); + reader_sp->Notify (eInputReaderActivate); + ActivateInputReader (reader_sp); +} + +bool +Debugger::PopInputReader (const lldb::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_readers.empty()) + { + InputReaderSP reader_sp(m_input_readers.top()); + + if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) + { + m_input_readers.pop (); + reader_sp->Notify (eInputReaderDeactivate); + reader_sp->Notify (eInputReaderDone); + result = true; + + if (!m_input_readers.empty()) + { + reader_sp = m_input_readers.top(); + if (reader_sp) + { + ActivateInputReader (reader_sp); + reader_sp->Notify (eInputReaderReactivate); + } + } + } + } + return result; +} + +bool +Debugger::CheckIfTopInputReaderIsDone () +{ + bool result = false; + if (!m_input_readers.empty()) + { + InputReaderSP reader_sp(m_input_readers.top()); + + if (reader_sp && reader_sp->IsDone()) + { + result = true; + PopInputReader (reader_sp); + } + } + return result; +} + +void +Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +{ + FILE *in_fh = GetInputFileHandle(); + + if (in_fh) + { + struct termios in_fh_termios; + int in_fd = fileno (in_fh); + if (::tcgetattr(in_fd, &in_fh_termios) == 0) + { + if (reader_sp->GetEcho()) + in_fh_termios.c_lflag |= ECHO; // Turn on echoing + else + in_fh_termios.c_lflag &= ~ECHO; // Turn off echoing + + switch (reader_sp->GetGranularity()) + { + case eInputReaderGranularityByte: + case eInputReaderGranularityWord: + in_fh_termios.c_lflag &= ~ICANON; // Get one char at a time + break; + + case eInputReaderGranularityLine: + case eInputReaderGranularityAll: + in_fh_termios.c_lflag |= ICANON; // Get lines at a time + break; + + default: + break; + } + ::tcsetattr (in_fd, TCSANOW, &in_fh_termios); + } + } +} diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp new file mode 100644 index 00000000000..570ff726225 --- /dev/null +++ b/lldb/source/Core/Disassembler.cpp @@ -0,0 +1,299 @@ +//===-- 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/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/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Timer.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; + + +Disassembler* +Disassembler::FindPlugin (const ArchSpec &arch) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "Disassembler::FindPlugin (arch = %s)", + arch.AsCString()); + + std::auto_ptr<Disassembler> disassembler_ap; + DisassemblerCreateInstance create_callback; + for (uint32_t idx = 0; (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + disassembler_ap.reset (create_callback(arch)); + + if (disassembler_ap.get()) + return disassembler_ap.release(); + } + return NULL; +} + +bool +Disassembler::Disassemble +( + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t mixed_context_lines, + Stream &strm +) +{ + Disassembler *disassembler = Disassembler::FindPlugin(arch); + + if (disassembler) + { + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + size_t byte_size = 0; + if (exe_ctx.frame) + { + SymbolContext sc(exe_ctx.frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); + if (sc.function) + { + addr = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + byte_size = sc.function->GetAddressRange().GetByteSize(); + } + else if (sc.symbol && sc.symbol->GetAddressRangePtr()) + { + addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + { + byte_size = sc.symbol->GetAddressRangePtr()->GetByteSize(); + if (byte_size == 0) + byte_size = DEFAULT_DISASM_BYTE_SIZE; + } + } + else + { + addr = exe_ctx.frame->GetPC().GetLoadAddress(exe_ctx.process); + if (addr != LLDB_INVALID_ADDRESS) + byte_size = DEFAULT_DISASM_BYTE_SIZE; + } + } + + if (byte_size) + { + DataExtractor data; + size_t bytes_disassembled = disassembler->ParseInstructions (&exe_ctx, eAddressTypeLoad, addr, byte_size, data); + if (bytes_disassembled == 0) + { + return false; + } + else + { + // We got some things disassembled... + size_t num_instructions = disassembler->GetInstructionList().GetSize(); + uint32_t offset = 0; + SymbolContext sc; + SymbolContext prev_sc; + AddressRange sc_range; + if (mixed_context_lines) + strm.IndentMore (); + + for (size_t i=0; i<num_instructions; ++i) + { + Disassembler::Instruction *inst = disassembler->GetInstructionList().GetInstructionAtIndex (i); + if (inst) + { + lldb::addr_t curr_addr = addr + offset; + if (mixed_context_lines) + { + if (!sc_range.ContainsLoadAddress (curr_addr, exe_ctx.process)) + { + prev_sc = sc; + Address curr_so_addr; + Process *process = exe_ctx.process; + if (process && process->ResolveLoadAddress (curr_addr, curr_so_addr)) + { + if (curr_so_addr.GetSection()) + { + Module *module = curr_so_addr.GetSection()->GetModule(); + uint32_t resolved_mask = module->ResolveSymbolContextForAddress(curr_so_addr, eSymbolContextEverything, sc); + if (resolved_mask) + { + sc.GetAddressRange (eSymbolContextEverything, sc_range); + if (sc != prev_sc) + { + if (offset != 0) + strm.EOL(); + + sc.DumpStopContext(&strm, process, curr_so_addr); + + if (sc.comp_unit && sc.line_entry.IsValid()) + { + Debugger::GetSharedInstance().GetSourceManager().DisplaySourceLinesWithLineNumbers ( + sc.line_entry.file, + sc.line_entry.line, + mixed_context_lines, + mixed_context_lines, + mixed_context_lines ? "->" : "", + &strm); + } + } + } + } + } + } + } + if (mixed_context_lines) + strm.IndentMore (); + strm.Indent(); + size_t inst_byte_size = inst->GetByteSize(); + //inst->Dump(&strm, curr_addr, &data, offset); // Do dump opcode bytes + inst->Dump(&strm, curr_addr, NULL, offset, exe_ctx, false); // Don't dump opcode bytes + strm.EOL(); + offset += inst_byte_size; + if (mixed_context_lines) + strm.IndentLess (); + } + else + { + break; + } + } + if (mixed_context_lines) + strm.IndentLess (); + + } + } + return true; + } + return false; +} + +Disassembler::Instruction::Instruction() +{ +} + +Disassembler::Instruction::~Instruction() +{ +} + + +Disassembler::InstructionList::InstructionList() : + m_instructions() +{ +} + +Disassembler::InstructionList::~InstructionList() +{ +} + +size_t +Disassembler::InstructionList::GetSize() const +{ + return m_instructions.size(); +} + + +Disassembler::Instruction * +Disassembler::InstructionList::GetInstructionAtIndex (uint32_t idx) +{ + if (idx < m_instructions.size()) + return m_instructions[idx].get(); + return NULL; +} + +const Disassembler::Instruction * +Disassembler::InstructionList::GetInstructionAtIndex (uint32_t idx) const +{ + if (idx < m_instructions.size()) + return m_instructions[idx].get(); + return NULL; +} + +void +Disassembler::InstructionList::Clear() +{ + m_instructions.clear(); +} + +void +Disassembler::InstructionList::AppendInstruction (Instruction::shared_ptr &inst_sp) +{ + if (inst_sp) + m_instructions.push_back(inst_sp); +} + + +size_t +Disassembler::ParseInstructions +( + const ExecutionContext *exe_ctx, + lldb::AddressType addr_type, + lldb::addr_t addr, + size_t byte_size, + DataExtractor& data +) +{ + Process *process = exe_ctx->process; + + if (process == NULL) + return 0; + + DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0')); + + Error error; + if (process->GetTarget().ReadMemory (addr_type, addr, data_sp->GetBytes(), data_sp->GetByteSize(), error, NULL)) + { + data.SetData(data_sp); + data.SetByteOrder(process->GetByteOrder()); + data.SetAddressByteSize(process->GetAddressByteSize()); + return ParseInstructions (data, 0, UINT32_MAX, addr); + } + + return 0; +} + +//---------------------------------------------------------------------- +// Disassembler copy constructor +//---------------------------------------------------------------------- +Disassembler::Disassembler(const ArchSpec& arch) : + m_arch (arch), + m_instruction_list(), + m_base_addr(LLDB_INVALID_ADDRESS) +{ + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Disassembler::~Disassembler() +{ +} + +Disassembler::InstructionList & +Disassembler::GetInstructionList () +{ + return m_instruction_list; +} + +const Disassembler::InstructionList & +Disassembler::GetInstructionList () const +{ + return m_instruction_list; +} diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp new file mode 100644 index 00000000000..b6b2f27caef --- /dev/null +++ b/lldb/source/Core/DynamicLoader.cpp @@ -0,0 +1,75 @@ +//===-- 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/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) + { + create_callback = PluginManager::GetDynamicLoaderCreateCallbackForPluginName (plugin_name); + if (create_callback) + { + std::auto_ptr<DynamicLoader> instance_ap(create_callback(process)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::auto_ptr<DynamicLoader> instance_ap(create_callback(process)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// DynamicLoader constructor +//---------------------------------------------------------------------- +DynamicLoader::DynamicLoader(Process *process) : + m_process (process), + m_stop_when_images_change(false) // Stop the process by default when a process' images change +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoader::~DynamicLoader() +{ +} + +//---------------------------------------------------------------------- +// Accessosors to the global setting as to wether to stop at image +// (shared library) loading/unloading. +//---------------------------------------------------------------------- +bool +DynamicLoader::GetStopWhenImagesChange () const +{ + return m_stop_when_images_change; +} + +void +DynamicLoader::SetStopWhenImagesChange (bool stop) +{ + m_stop_when_images_change = stop; +} + diff --git a/lldb/source/Core/Error.cpp b/lldb/source/Core/Error.cpp new file mode 100644 index 00000000000..c3522093f9b --- /dev/null +++ b/lldb/source/Core/Error.cpp @@ -0,0 +1,365 @@ +//===-- 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 <sys/errno.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" + +#if defined (__arm__) +#include <SpringBoardServices/SpringBoardServer.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Error::Error(ValueType err, ErrorType type) : + m_code (err), + m_type (type), + m_string () +{ +} +//---------------------------------------------------------------------- +// 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 = (kern_return_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: + s = ::mach_error_string (m_code); + 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::SetError (kern_return_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; + m_string.append("\n"); + } + 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... + m_string.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); + int length = ::vsnprintf (&m_string[0], m_string.size(), format, args); + if (length < m_string.size()) + { + // The error formatted string fit into our buffer, just chop it down + // to size + m_string.erase (length); + } + else + { + // The error formatted string didn't fit into our buffer, resize it + // to the exact needed size, and retry + m_string.resize(length + 1); + length = ::vsnprintf (&m_string[0], m_string.size(), format, copy_args); + va_end (copy_args); + assert (length < m_string.size()); + } + 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; +} diff --git a/lldb/source/Core/Event.cpp b/lldb/source/Core/Event.cpp new file mode 100644 index 00000000000..c2bf08d8deb --- /dev/null +++ b/lldb/source/Core/Event.cpp @@ -0,0 +1,241 @@ +//===-- 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/Target/Process.h" + +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::Clear() +{ + m_data_ap.reset(); +} + +void +Event::Dump (Stream *s) const +{ + s->Printf("%p Event: broadcaster = %p, type = 0x%8.8x, data = ", this, m_broadcaster, m_type); + + if (m_data_ap.get() == NULL) + s->Printf ("<NULL>"); + else + { + s->PutChar('{'); + m_data_ap->Dump (s); + s->PutChar('}'); + } +} + +Broadcaster * +Event::GetBroadcaster () const +{ + return m_broadcaster; +} + +bool +Event::BroadcasterIs (Broadcaster *broadcaster) +{ + return broadcaster == m_broadcaster; +} + +uint32_t +Event::GetType() const +{ + return m_type; +} + + +EventData * +Event::GetData () +{ + return m_data_ap.get(); +} + +const EventData * +Event::GetData () const +{ + return m_data_ap.get(); +} + +void +Event::DoOnRemoval () +{ + if (m_data_ap.get()) + m_data_ap->DoOnRemoval (this); +} + +void +Event::SetBroadcaster (Broadcaster *broadcaster) +{ + m_broadcaster = broadcaster; +} + +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 + { + DataExtractor data; + data.SetData(m_bytes.data(), m_bytes.size(), eByteOrderHost); + 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.data(); +} + +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; +} + diff --git a/lldb/source/Core/FileSpec.cpp b/lldb/source/Core/FileSpec.cpp new file mode 100644 index 00000000000..305650d8643 --- /dev/null +++ b/lldb/source/Core/FileSpec.cpp @@ -0,0 +1,580 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include <fcntl.h> +#include <glob.h> +#include <libgen.h> +#include <stdlib.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <fstream> + +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +static bool +GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) +{ + char resolved_path[PATH_MAX]; + if (file_spec->GetPath(&resolved_path[0], sizeof(resolved_path))) + return ::stat (resolved_path, stats_ptr) == 0; + return false; +} + +static const char* +GetCachedGlobTildeSlash() +{ + static std::string g_tilde; + if (g_tilde.empty()) + { + glob_t globbuf; + if (::glob("~/", GLOB_TILDE, NULL, &globbuf) == 0) //success + { + g_tilde = globbuf.gl_pathv[0]; + ::globfree (&globbuf); + } + if (g_tilde.empty()) + return NULL; + } + return g_tilde.c_str(); +} + +int +FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len) +{ + if (src_path == NULL || src_path[0] == '\0') + return 0; + + // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path... + char unglobbed_path[PATH_MAX]; + if (::strstr (src_path, "~/") == src_path) + ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s%s", GetCachedGlobTildeSlash(), src_path + 2); + else + ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path); + + // Now resolve the path if needed + char resolved_path[PATH_MAX]; + if (::realpath (unglobbed_path, resolved_path)) + { + // Success, copy the resolved path + return ::snprintf(dst_path, dst_len, "%s", resolved_path); + } + else + { + // Failed, just copy the unglobbed path + return ::snprintf(dst_path, dst_len, "%s", unglobbed_path); + } +} + +FileSpec::FileSpec() : + m_directory(), + m_filename() +{ +} + +//------------------------------------------------------------------ +// Default constructor that can take an optional full path to a +// file on disk. +//------------------------------------------------------------------ +FileSpec::FileSpec(const char *pathname) : + m_directory(), + m_filename() +{ + if (pathname && pathname[0]) + SetFile(pathname); +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec& rhs) : + m_directory (rhs.m_directory), + m_filename (rhs.m_filename) +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec* rhs) : + m_directory(), + m_filename() +{ + if (rhs) + *this = *rhs; +} + +//------------------------------------------------------------------ +// Virtual destrcuctor in case anyone inherits from this class. +//------------------------------------------------------------------ +FileSpec::~FileSpec() +{ +} + +//------------------------------------------------------------------ +// Assignment operator. +//------------------------------------------------------------------ +const FileSpec& +FileSpec::operator= (const FileSpec& rhs) +{ + if (this != &rhs) + { + m_directory = rhs.m_directory; + m_filename = rhs.m_filename; + } + return *this; +} + + +//------------------------------------------------------------------ +// Update the contents of this object with a new path. The path will +// be split up into a directory and filename and stored as uniqued +// string values for quick comparison and efficient memory usage. +//------------------------------------------------------------------ +void +FileSpec::SetFile(const char *pathname) +{ + m_filename.Clear(); + m_directory.Clear(); + if (pathname == NULL || pathname[0] == '\0') + return; + + char resolved_path[PATH_MAX]; + + if (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1) + { + char *filename = ::basename (resolved_path); + if (filename) + { + m_filename.SetCString (filename); + // Truncate the basename off the end of the resolved path + + // Only attempt to get the dirname if it looks like we have a path + if (strchr(resolved_path, '/')) + { + char *directory = ::dirname (resolved_path); + + // Make sure we didn't get our directory resolved to "." without having + // specified + if (directory) + m_directory.SetCString(directory); + else + { + char *last_resolved_path_slash = strrchr(resolved_path, '/'); + if (last_resolved_path_slash) + { + *last_resolved_path_slash = '\0'; + m_directory.SetCString(resolved_path); + } + } + } + } + else + m_directory.SetCString(resolved_path); + } +} + +//---------------------------------------------------------------------- +// Convert to pointer operator. This allows code to check any FileSpec +// objects to see if they contain anything valid using code such as: +// +// if (file_spec) +// {} +//---------------------------------------------------------------------- +FileSpec::operator +void*() const +{ + return (m_directory || m_filename) ? const_cast<FileSpec*>(this) : NULL; +} + +//---------------------------------------------------------------------- +// Logical NOT operator. This allows code to check any FileSpec +// objects to see if they are invalid using code such as: +// +// if (!file_spec) +// {} +//---------------------------------------------------------------------- +bool +FileSpec::operator!() const +{ + return !m_directory && !m_filename; +} + +//------------------------------------------------------------------ +// Equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator== (const FileSpec& rhs) const +{ + return m_directory == rhs.m_directory && m_filename == rhs.m_filename; +} + +//------------------------------------------------------------------ +// Not equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator!= (const FileSpec& rhs) const +{ + return m_filename != rhs.m_filename || m_directory != rhs.m_directory; +} + +//------------------------------------------------------------------ +// Less than operator +//------------------------------------------------------------------ +bool +FileSpec::operator< (const FileSpec& rhs) const +{ + return FileSpec::Compare(*this, rhs, true) < 0; +} + +//------------------------------------------------------------------ +// Dump a FileSpec object to a stream +//------------------------------------------------------------------ +Stream& +lldb_private::operator << (Stream &s, const FileSpec& f) +{ + f.Dump(&s); + return s; +} + +//------------------------------------------------------------------ +// Clear this object by releasing both the directory and filename +// string values and making them both the empty string. +//------------------------------------------------------------------ +void +FileSpec::Clear() +{ + m_directory.Clear(); + m_filename.Clear(); +} + +//------------------------------------------------------------------ +// Compare two FileSpec objects. If "full" is true, then both +// the directory and the filename must match. If "full" is false, +// then the directory names for "a" and "b" are only compared if +// they are both non-empty. This allows a FileSpec object to only +// contain a filename and it can match FileSpec objects that have +// matching filenames with different paths. +// +// Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" +// and "1" if "a" is greater than "b". +//------------------------------------------------------------------ +int +FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) +{ + int result = 0; + + // If full is true, then we must compare both the directory and filename. + + // If full is false, then if either directory is empty, then we match on + // the basename only, and if both directories have valid values, we still + // do a full compare. This allows for matching when we just have a filename + // in one of the FileSpec objects. + + if (full || (a.m_directory && b.m_directory)) + { + result = ConstString::Compare(a.m_directory, b.m_directory); + if (result) + return result; + } + return ConstString::Compare (a.m_filename, b.m_filename); +} + +bool +FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) +{ + if (full) + return a == b; + else + return a.m_filename == b.m_filename; +} + + + +//------------------------------------------------------------------ +// Dump the object to the supplied stream. If the object contains +// a valid directory name, it will be displayed followed by a +// directory delimiter, and the filename. +//------------------------------------------------------------------ +void +FileSpec::Dump(Stream *s) const +{ + if (m_filename) + m_directory.Dump(s, ""); // Provide a default for m_directory when we dump it in case it is invalid + + if (m_directory) + { + // If dirname was valid, then we need to print a slash between + // the directory and the filename + s->PutChar('/'); + } + m_filename.Dump(s); +} + +//------------------------------------------------------------------ +// Returns true if the file exists. +//------------------------------------------------------------------ +bool +FileSpec::Exists () const +{ + struct stat file_stats; + return GetFileStats (this, &file_stats); +} + +uint64_t +FileSpec::GetByteSize() const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + return file_stats.st_size; + return 0; +} + +FileSpec::FileType +FileSpec::GetFileType () const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + { + mode_t file_type = file_stats.st_mode & S_IFMT; + switch (file_type) + { + case S_IFDIR: return eFileTypeDirectory; + case S_IFIFO: return eFileTypePipe; + case S_IFREG: return eFileTypeRegular; + case S_IFSOCK: return eFileTypeSocket; + case S_IFLNK: return eFileTypeSymbolicLink; + default: + break; + } + return eFileTypeUknown; + } + return eFileTypeInvalid; +} + +TimeValue +FileSpec::GetModificationTime () const +{ + TimeValue mod_time; + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + mod_time = file_stats.st_mtimespec; + return mod_time; +} + +//------------------------------------------------------------------ +// Directory string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetDirectory() +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Directory string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetDirectory() const +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Filename string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetFilename() +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Filename string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetFilename() const +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Extract the directory and path into a fixed buffer. This is +// needed as the directory and path are stored in separate string +// values. +//------------------------------------------------------------------ +bool +FileSpec::GetPath(char *path, size_t max_path_length) const +{ + if (max_path_length == 0) + return false; + + path[0] = '\0'; + const char *dirname = m_directory.AsCString(); + const char *filename = m_filename.AsCString(); + if (dirname) + { + if (filename && filename[0]) + { + return snprintf (path, max_path_length, "%s/%s", dirname, filename) < max_path_length; + } + else + { + strncpy (path, dirname, max_path_length); + } + } + else if (filename) + { + strncpy (path, filename, max_path_length); + } + + // Any code paths that reach here assume that strncpy, or a similar function was called + // where any remaining bytes will be filled with NULLs and that the string won't be + // NULL terminated if it won't fit in the buffer. + + // If the last character is NULL, then all went well + if (path[max_path_length-1] == '\0') + return true; + + // Make sure the path is terminated, as it didn't fit into "path" + path[max_path_length-1] = '\0'; + return false; +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data is memory mapped and +// will lazily page in data from the file as memory is accessed. +// The data that is mappped will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const +{ + DataBufferSP data_sp; + auto_ptr<DataBufferMemoryMap> mmap_data(new DataBufferMemoryMap()); + if (mmap_data.get()) + { + if (mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size) >= file_size) + data_sp.reset(mmap_data.release()); + } + return data_sp; +} + + +//------------------------------------------------------------------ +// Return the size in bytes that this object takes in memory. This +// returns the size in bytes of this object, not any shared string +// values it may refer to. +//------------------------------------------------------------------ +size_t +FileSpec::MemorySize() const +{ + return m_filename.MemorySize() + m_directory.MemorySize(); +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data copies into a heap based +// buffer that lives in the DataBuffer shared pointer object returned. +// The data that is cached will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::ReadFileContents(off_t file_offset, size_t file_size) const +{ + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + int fd = ::open (resolved_path, O_RDONLY, 0); + if (fd != -1) + { + struct stat file_stats; + if (::fstat (fd, &file_stats) == 0) + { + // Read bytes directly into our basic_string buffer + if (file_stats.st_size > 0) + { + off_t lseek_result = 0; + if (file_offset > 0) + lseek_result = ::lseek (fd, file_offset, SEEK_SET); + + if (lseek_result < 0) + { + // Get error from errno + } + else if (lseek_result == file_offset) + { + std::auto_ptr<DataBufferHeap> data_heap_ap; + if (file_stats.st_size < file_size) + data_heap_ap.reset(new DataBufferHeap(file_stats.st_size, '\0')); + else + data_heap_ap.reset(new DataBufferHeap(file_size, '\0')); + + if (data_heap_ap.get()) + { + ssize_t bytesRead = ::read (fd, (void *)data_heap_ap->GetBytes(), data_heap_ap->GetByteSize()); + if (bytesRead >= 0) + { + // Make sure we read exactly what we asked for and if we got + // less, adjust the array + if (bytesRead < data_heap_ap->GetByteSize()) + data_heap_ap->SetByteSize(bytesRead); + data_sp.reset(data_heap_ap.release()); + } + } + } + } + } + } + close(fd); + } + return data_sp; +} + +bool +FileSpec::ReadFileLines (STLStringArray &lines) +{ + bool ret_val = false; + lines.clear(); + + std::string dir_str (m_directory.AsCString()); + std::string file_str (m_filename.AsCString()); + std::string full_name = dir_str + "/" + file_str; + + ifstream file_stream (full_name.c_str()); + + if (file_stream) + { + std::string line; + while (getline (file_stream, line)) + lines.push_back (line); + ret_val = true; + } + + return ret_val; +} diff --git a/lldb/source/Core/FileSpecList.cpp b/lldb/source/Core/FileSpecList.cpp new file mode 100644 index 00000000000..17abf4b24e5 --- /dev/null +++ b/lldb/source/Core/FileSpecList.cpp @@ -0,0 +1,228 @@ +//===-- 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" + +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 +{ + for_each (m_files.begin(), m_files.end(), bind2nd(mem_fun_ref(&FileSpec::Dump),s)); +} + +//------------------------------------------------------------------ +// 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. +//------------------------------------------------------------------ +uint32_t +FileSpecList::FindFileIndex (uint32_t start_idx, const FileSpec &file_spec) const +{ + const uint32_t num_files = m_files.size(); + uint32_t idx; + + // 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 (idx = start_idx; idx < num_files; ++idx) + { + if (compare_filename_only) + { + if (m_files[idx].GetFilename() == file_spec.GetFilename()) + return idx; + } + else + { + if (m_files[idx] == file_spec) + 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(uint32_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(uint32_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. +//------------------------------------------------------------------ +uint32_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/lldb/source/Core/Flags.cpp b/lldb/source/Core/Flags.cpp new file mode 100644 index 00000000000..13cbd85915b --- /dev/null +++ b/lldb/source/Core/Flags.cpp @@ -0,0 +1,122 @@ +//===-- Flags.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/Flags.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +Flags::Flags (ValueType flags) : + m_flags(flags) +{ +} + +//---------------------------------------------------------------------- +// Copy Constructor +//---------------------------------------------------------------------- +Flags::Flags (const Flags& rhs) : + m_flags(rhs.m_flags) +{ +} + +//---------------------------------------------------------------------- +// Virtual destructor in case anyone inherits from this class. +//---------------------------------------------------------------------- +Flags::~Flags () +{ +} + +//---------------------------------------------------------------------- +// Get accessor for all of the current flag bits. +//---------------------------------------------------------------------- +Flags::ValueType +Flags::GetAllFlagBits () const +{ + return m_flags; +} + +size_t +Flags::GetBitSize() const +{ + return sizeof (ValueType) * 8; +} + +//---------------------------------------------------------------------- +// Set accessor for all of the current flag bits. +//---------------------------------------------------------------------- +void +Flags::SetAllFlagBits (ValueType flags) +{ + m_flags = flags; +} + +//---------------------------------------------------------------------- +// Clear one or more bits in our flag bits +//---------------------------------------------------------------------- +Flags::ValueType +Flags::Clear (ValueType bits) +{ + m_flags &= ~bits; + return m_flags; +} + +//---------------------------------------------------------------------- +// Set one or more bits in our flag bits +//---------------------------------------------------------------------- +Flags::ValueType +Flags::Set (ValueType bits) +{ + m_flags |= bits; + return m_flags; +} + +//---------------------------------------------------------------------- +// Returns true if any flag bits in "bits" are set +//---------------------------------------------------------------------- +bool +Flags::IsSet (ValueType bits) const +{ + return (m_flags & bits) != 0; +} + +//---------------------------------------------------------------------- +// Returns true if all flag bits in "bits" are clear +//---------------------------------------------------------------------- +bool +Flags::IsClear (ValueType bits) const +{ + return (m_flags & bits) == 0; +} + + +size_t +Flags::SetCount () const +{ + size_t count = 0; + for (ValueType mask = m_flags; mask; mask >>= 1) + { + if (mask & 1) + ++count; + } + return count; +} + +size_t +Flags::ClearCount () const +{ + size_t count = 0; + for (ValueType shift = 0; shift < sizeof(ValueType)*8; ++shift) + { + if ((m_flags & (1u << shift)) == 0) + ++count; + } + return count; +} diff --git a/lldb/source/Core/InputReader.cpp b/lldb/source/Core/InputReader.cpp new file mode 100644 index 00000000000..c139a87387a --- /dev/null +++ b/lldb/source/Core/InputReader.cpp @@ -0,0 +1,343 @@ +//===-- 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 <string> + +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Debugger.h" + +using namespace lldb; +using namespace lldb_private; + +InputReader::InputReader () : + m_callback (NULL), + m_callback_baton (NULL), + m_end_token (), + m_granularity (eInputReaderGranularityInvalid), + m_done (true), + m_echo (true), + m_active (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) + { + 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; + const char *end = bytes + bytes_len; + 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; +} + + +FILE * +InputReader::GetInputFileHandle () +{ + return Debugger::GetSharedInstance().GetInputFileHandle (); +} + +FILE * +InputReader::GetOutputFileHandle () +{ + return Debugger::GetSharedInstance().GetOutputFileHandle (); +} + +const char * +InputReader::GetPrompt () const +{ + if (!m_prompt.empty()) + return m_prompt.c_str(); + else + return NULL; +} + +void +InputReader::RefreshPrompt () +{ + if (!m_prompt.empty()) + { + FILE *out_fh = GetOutputFileHandle(); + if (out_fh) + ::fprintf (out_fh, "%s", m_prompt.c_str()); + } +} + +void +InputReader::Notify (InputReaderAction notification) +{ + switch (notification) + { + case eInputReaderActivate: + case eInputReaderReactivate: + m_active = true; + break; + + case eInputReaderDeactivate: + case eInputReaderDone: + m_active = false; + 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); +} diff --git a/lldb/source/Core/Language.cpp b/lldb/source/Core/Language.cpp new file mode 100644 index 00000000000..82b4e4f3aca --- /dev/null +++ b/lldb/source/Core/Language.cpp @@ -0,0 +1,150 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +#define ENUM_TO_DCSTREAM(x) case x: s->PutCString(#x); return + +typedef 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 uint32_t +g_num_languages = sizeof(g_languages)/sizeof(LanguageStrings); + +Language::Language(Language::Type language) : + m_language (language) +{ +} + +Language::~Language() +{ +} + +Language::Type +Language::GetLanguage() const +{ + return m_language; +} + +void +Language::Clear () +{ + m_language = Unknown; +} + +void +Language::SetLanguage(Language::Type 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 = (Language::Type)i; + return true; + } + } + } + + m_language = Unknown; + 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/lldb/source/Core/Listener.cpp b/lldb/source/Core/Listener.cpp new file mode 100644 index 00000000000..639b74a4c7d --- /dev/null +++ b/lldb/source/Core/Listener.cpp @@ -0,0 +1,480 @@ +//===-- 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" + +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); + 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(); +} + +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::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 (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.Reset(); + 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) + { + 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 + m_cond_wait.SetValue (false, eBroadcastNever); + + if (m_cond_wait.WaitForValueEqualTo (true, timeout, &timed_out)) + continue; + + else if (timed_out) + { + if (log) + log->Printf ("%p Listener::WaitForEvents() timed out for %s", this, m_name.c_str()); + break; + } + else + { + if (log) + log->Printf ("%p Listener::WaitForEvents() 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; +} diff --git a/lldb/source/Core/Log.cpp b/lldb/source/Core/Log.cpp new file mode 100644 index 00000000000..fe2071683ca --- /dev/null +++ b/lldb/source/Core/Log.cpp @@ -0,0 +1,590 @@ +//===-- Log.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 <mach/mach.h> +#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" + +using namespace lldb; +using namespace lldb_private; + +static Stream * +StreamForSTDOUTAccess (bool set, StreamSP &stream_sp) +{ + // Since we are in a shared library and we can't have global + // constructors, we need to control access to this static variable + // through an accessor function to get and set the value. + static StreamSP g_stream_sp; + + if (set) + g_stream_sp = stream_sp; + else + { + if (g_stream_sp) + stream_sp = g_stream_sp; + else + { + FILE *out_fh = Debugger::GetSharedInstance().GetOutputFileHandle(); + if (out_fh) + stream_sp.reset(new StreamFile(out_fh)); + else + stream_sp.reset(); + } + } + return stream_sp.get(); +} + +StreamSP +Log::GetStreamForSTDOUT () +{ + StreamSP stream_sp; + StreamForSTDOUTAccess (false, stream_sp); + return stream_sp; +} + +void +Log::SetStreamForSTDOUT (StreamSP &stream_sp) +{ + StreamForSTDOUTAccess (true, stream_sp); +} + +void +Log::STDOUT (const char *format, ...) +{ + StreamSP stream_sp; + if (StreamForSTDOUTAccess(false, stream_sp)) + { + va_list args; + va_start (args, format); + stream_sp->PrintfVarArg(format, args); + va_end (args); + } +} + +static Stream * +StreamForSTDERRAccess (bool set, StreamSP &stream_sp) +{ + // Since we are in a shared library and we can't have global + // constructors, we need to control access to this static variable + // through an accessor function to get and set the value. + static StreamSP g_stream_sp(new StreamFile(Debugger::GetSharedInstance().GetErrorFileHandle())); + + if (set) + g_stream_sp = stream_sp; + else + stream_sp = g_stream_sp; + return stream_sp.get(); +} + +StreamSP +Log::GetStreamForSTDERR () +{ + StreamSP stream_sp; + StreamForSTDERRAccess (false, stream_sp); + return stream_sp; +} + +void +Log::SetStreamForSTDERR (StreamSP &stream_sp) +{ + StreamForSTDERRAccess (true, stream_sp); +} + +void +Log::STDERR (const char *format, ...) +{ + StreamSP stream_sp; + if (StreamForSTDERRAccess(false, stream_sp)) + { + va_list args; + va_start (args, format); + stream_sp->PrintfVarArg(format, args); + va_end (args); + } +} + +Log::Log () : + m_stream_sp(), + m_options(0), + m_mask_bits(0) +{ +} + +Log::Log (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; + static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); + + Mutex::Locker locker; + + uint32_t log_options = m_options.GetAllFlagBits(); + + // Lock the threaded logging mutex if we are doing thread safe logging + if (log_options & LLDB_LOG_OPTION_THREADSAFE) + locker.Reset(g_LogThreadedMutex.GetMutex()); + + // Add a sequence ID if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_SEQUENCE) + header.Printf ("%u ", ++g_sequence_id); + + // Timestamp if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_TIMESTAMP) + { + struct timeval tv = TimeValue::Now().GetAsTimeVal(); + header.Printf ("%9llu.%6.6llu ", tv.tv_sec, tv.tv_usec); + } + + // Add the process and thread if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD) + header.Printf ("[%4.4x/%4.4x]: ", getpid(), mach_thread_self()); + + // Add the process and thread if requested + if (log_options & LLDB_LOG_OPTION_PREPEND_THREAD_NAME) + { + const char *thread_name_str = Host::GetThreadName (getpid(), mach_thread_self()); + if (thread_name_str) + header.Printf ("%s ", thread_name_str); + } + + header.PrintfVarArg (format, args); + m_stream_sp->Printf("%s\n", header.GetData()); + } +} + + +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().IsSet(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().IsSet(LLDB_LOG_OPTION_DEBUG) && GetOptions().IsSet(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 ((bits & m_options.GetAllFlagBits()) == 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.IsSet(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.IsSet(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 <std::string, 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 char *channel, const Log::Callbacks &log_callbacks) +{ + GetCallbackMap().insert(std::make_pair(channel, log_callbacks)); +} + +bool +Log::UnregisterLogChannel (const char *channel) +{ + return GetCallbackMap().erase(channel) != 0; +} + +bool +Log::GetLogChannelCallbacks (const char *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; + } + ::bzero (&log_callbacks, sizeof(log_callbacks)); + return false; +} + +void +Log::EnableAllLogChannels +( + StreamSP &log_stream_sp, + uint32_t log_options, + Args &args, + 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, args, 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, args); + } + +} + +void +Log::DisableAllLogChannels () +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.disable (); + + 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 (); +} + +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 +{ + 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()) + { + LogChannelCreateInstance create_callback = PluginManager::GetLogChannelCreateCallbackForPluginName (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_sp () +{ +} + +LogChannel::~LogChannel () +{ +} + +const char * +LogChannel::GetPluginSuffix () +{ + return ".log-channel"; +} + + diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp new file mode 100644 index 00000000000..38667a86def --- /dev/null +++ b/lldb/source/Core/Mangled.cpp @@ -0,0 +1,733 @@ +//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cxxabi.h> + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +using namespace lldb_private; + +#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 char *s, bool mangled) : + m_mangled(), + m_demangled() +{ + if (s && s[0]) + { + SetValue(s, mangled); + } +} + +//---------------------------------------------------------------------- +// 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(), a.GetName()); +} + + + +//---------------------------------------------------------------------- +// 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 char *s, bool mangled) +{ + m_mangled.Clear(); + m_demangled.Clear(); + + if (s) + { + if (mangled) + m_mangled.SetCString (s); + else + m_demangled.SetCString(s); + } +} + + +//---------------------------------------------------------------------- +// 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()); + + // We already know mangled is valid from the above check, + // lets just make sure it isn't empty... + const char * mangled = m_mangled.AsCString(); + if (mangled[0]) + { + // The first time the demangling routine is called, it will + // return a buffer value and length and we will continue to + // re-use that buffer so we don't always have to malloc/free + // a buffer for each demangle. The buffer can be realloc'ed + // by abi::__cxa_demangle, so we may need to make it thread + // specific if we ever start doing multi-threaded calls to + // this function. g_demangle_buf will currently leak one + // malloc entry that can vary in size. If we need to reclaim + // this memory, we will need to add some code to free this + // buffer at exit time. + static char *g_demangle_buf = NULL; + static size_t g_demangle_buf_len = 0; + int status = 0; + g_demangle_buf = abi::__cxa_demangle(mangled, g_demangle_buf, &g_demangle_buf_len, &status); + if (g_demangle_buf != NULL) + { + m_demangled.SetCString(g_demangle_buf); + } + else + { + // Set the demangled string to the empty string to indicate we + // tried to parse it once and failed. + m_demangled.SetCString(""); + } + } + } + + return m_demangled; +} + +//---------------------------------------------------------------------- +// Mangled name get accessor +//---------------------------------------------------------------------- +ConstString& +Mangled::GetMangledName () +{ + return m_mangled; +} + +//---------------------------------------------------------------------- +// Mangled name const get accessor +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetMangledName () const +{ + return m_mangled; +} + +//---------------------------------------------------------------------- +// Get the demangled name if there is one, else return the mangled name. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetName () const +{ + const ConstString& name = GetDemangledName(); + if (name && !name.IsEmpty()) + return name; + return m_mangled; +} + +//---------------------------------------------------------------------- +// Generate the tokens from the demangled name. +// +// Returns the number of tokens that were parsed. +//---------------------------------------------------------------------- +size_t +Mangled::GetTokens (Mangled::TokenList &tokens) const +{ + tokens.Clear(); + const ConstString& demangled = GetDemangledName(); + if (demangled && !demangled.IsEmpty()) + tokens.Parse(demangled.AsCString()); + + return tokens.Size(); +} + +//---------------------------------------------------------------------- +// 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; +} + + + + +#pragma mark Mangled::Token + +//-------------------------------------------------------------- +// Default constructor +//-------------------------------------------------------------- +Mangled::Token::Token () : + type(eInvalid), + value() +{ +} + +//-------------------------------------------------------------- +// Equal to operator +//-------------------------------------------------------------- +bool +Mangled::Token::operator== (const Token& rhs) const +{ + return type == rhs.type && value == rhs.value; +} + +//-------------------------------------------------------------- +// Dump the token to a stream "s" +//-------------------------------------------------------------- +void +Mangled::Token::Dump (Stream *s) const +{ + switch (type) + { + case eInvalid: s->PutCString("invalid "); break; + case eNameSpace: s->PutCString("namespace "); break; + case eMethodName: s->PutCString("method "); break; + case eType: s->PutCString("type "); break; + case eTemplate: s->PutCString("template "); break; + case eTemplateBeg: s->PutCString("template < "); break; + case eTemplateEnd: s->PutCString("template > "); break; + case eParamsBeg: s->PutCString("params ( "); break; + case eParamsEnd: s->PutCString("params ) "); break; + case eQualifier: s->PutCString("qualifier "); break; + case eError: s->PutCString("ERROR "); break; + default: + s->Printf("type = %i", type); + break; + } + value.DumpDebug(s); +} + +//-------------------------------------------------------------- +// Returns true if this token is a wildcard +//-------------------------------------------------------------- +bool +Mangled::Token::IsWildcard () const +{ + static ConstString g_wildcard_str("*"); + return value == g_wildcard_str; +} + + +//---------------------------------------------------------------------- +// Dump "obj" to the supplied stream "s" +//---------------------------------------------------------------------- +Stream& +lldb_private::operator << (Stream& s, const Mangled::Token& obj) +{ + obj.Dump(&s); + return s; +} + + +#pragma mark Mangled::TokenList +//---------------------------------------------------------------------- +// Mangled::TokenList +//---------------------------------------------------------------------- + +//-------------------------------------------------------------- +// Default constructor. If demangled is non-NULL and not-empty +// the token list will parse up the demangled string it is +// given, else the object will initialize an empty token list. +//-------------------------------------------------------------- +Mangled::TokenList::TokenList (const char *demangled) : + m_tokens() +{ + if (demangled && demangled[0]) + { + Parse(demangled); + } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Mangled::TokenList::~TokenList () +{ +} + +//---------------------------------------------------------------------- +// Parses "demangled" into tokens. This allows complex +// comparisons to be done. Comparisons can include wildcards at +// the namespace, method name, template, and template and +// parameter type levels. +// +// Example queries include: +// "std::basic_string<*>" // Find all std::basic_string variants +// "std::basic_string<*>::erase(*)" // Find all std::basic_string::erase variants with any number of parameters +// "*::clear()" // Find all functions with a method name of +// // "clear" that are in any namespace that +// // have no parameters +// "::printf" // Find the printf function in the global namespace +// "printf" // Ditto +// "foo::*(int)" // Find all functions in the class or namespace "foo" that take a single integer argument +// +// Returns the number of tokens that were decoded, or zero when +// we fail. +//---------------------------------------------------------------------- +size_t +Mangled::TokenList::Parse (const char *s) +{ + m_tokens.clear(); + + Token token; + token.type = eNameSpace; + + TokenType max_type = eInvalid; + const char *p = s; + size_t span = 0; + size_t sep_size = 0; + + while (*p != '\0') + { + p = p + span + sep_size; + while (isspace(*p)) + ++p; + + if (*p == '\0') + break; + + span = strcspn(p, ":<>(),"); + sep_size = 1; + token.type = eInvalid; + switch (p[span]) + { + case '\0': + break; + + case ':': + if (p[span+1] == ':') + { + sep_size = 2; + if (span > 0) + { + token.type = eNameSpace; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + else + continue; + } + break; + + case '(': + if (span > 0) + { + token.type = eMethodName; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eParamsBeg; + token.value.Clear(); + m_tokens.push_back(token); + break; + + case ',': + if (span > 0) + { + token.type = eType; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + else + { + continue; + } + break; + + case ')': + if (span > 0) + { + token.type = eType; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eParamsEnd; + token.value.Clear(); + m_tokens.push_back(token); + break; + + case '<': + if (span > 0) + { + token.type = eTemplate; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eTemplateBeg; + token.value.Clear(); + m_tokens.push_back(token); + break; + + case '>': + if (span > 0) + { + token.type = eType; + token.value.SetCStringWithLength (p, span); + m_tokens.push_back(token); + } + + token.type = eTemplateEnd; + token.value.Clear(); + m_tokens.push_back(token); + break; + } + + if (max_type < token.type) + max_type = token.type; + + if (token.type == eInvalid) + { + if (max_type >= eParamsEnd) + { + token.type = eQualifier; + token.value.SetCString(p); + m_tokens.push_back(token); + } + else if (max_type >= eParamsBeg) + { + token.type = eType; + token.value.SetCString(p); + m_tokens.push_back(token); + } + else + { + token.type = eMethodName; + token.value.SetCString(p); + m_tokens.push_back(token); + } + break; + } + } + return m_tokens.size(); +} + + +//---------------------------------------------------------------------- +// Clear the token list. +//---------------------------------------------------------------------- +void +Mangled::TokenList::Clear () +{ + m_tokens.clear(); +} + +//---------------------------------------------------------------------- +// Dump the token list to the stream "s" +//---------------------------------------------------------------------- +void +Mangled::TokenList::Dump (Stream *s) const +{ + collection::const_iterator pos; + collection::const_iterator beg = m_tokens.begin(); + collection::const_iterator end = m_tokens.end(); + for (pos = beg; pos != end; ++pos) + { + s->Indent("token["); + *s << (uint32_t)std::distance(beg, pos) << "] = " << *pos << "\n"; + } +} + +//---------------------------------------------------------------------- +// Find the first token in the list that has "token_type" as its +// type +//---------------------------------------------------------------------- +const Mangled::Token * +Mangled::TokenList::Find (TokenType token_type) const +{ + collection::const_iterator pos; + collection::const_iterator beg = m_tokens.begin(); + collection::const_iterator end = m_tokens.end(); + for (pos = beg; pos != end; ++pos) + { + if (pos->type == token_type) + return &(*pos); + } + return NULL; +} + +//---------------------------------------------------------------------- +// Return the token at index "idx", or NULL if the index is +// out of range. +//---------------------------------------------------------------------- +const Mangled::Token * +Mangled::TokenList::GetTokenAtIndex (uint32_t idx) const +{ + if (idx < m_tokens.size()) + return &m_tokens[idx]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Given a token list, see if it matches this object's tokens. +// "token_list" can contain wild card values to enable powerful +// matching. Matching the std::string::erase(*) example that was +// tokenized above we could use a token list such as: +// +// token name +// ----------- ---------------------------------------- +// eNameSpace "std" +// eTemplate "basic_string" +// eTemplateBeg +// eInvalid "*" +// eTemplateEnd +// eMethodName "erase" +// eParamsBeg +// eInvalid "*" +// eParamsEnd +// +// Returns true if it "token_list" matches this object's tokens, +// false otherwise. +//---------------------------------------------------------------------- +bool +Mangled::TokenList::MatchesQuery (const Mangled::TokenList &match) const +{ + size_t match_count = 0; + collection::const_iterator pos; + collection::const_iterator pos_end = m_tokens.end(); + + collection::const_iterator match_pos; + collection::const_iterator match_pos_end = match.m_tokens.end(); + collection::const_iterator match_wildcard_pos = match_pos_end; + collection::const_iterator match_next_pos = match_pos_end; + + size_t template_scope_depth = 0; + + for (pos = m_tokens.begin(), match_pos = match.m_tokens.begin(); + pos != pos_end && match_pos != match_pos_end; + ++match_pos) + { + match_next_pos = match_pos + 1; + // Is this a wildcard? + if (match_pos->IsWildcard()) + { + if (match_wildcard_pos != match_pos_end) + return false; // Can't have two wildcards in effect at once. + + match_wildcard_pos = match_pos; + // Are we at the end of the MATCH token list? + if (match_next_pos == match_pos_end) + { + // There is nothing more to match, return if we have any matches so far... + return match_count > 0; + } + } + + if (match_pos->type == eInvalid || match_pos->type == eError) + { + return false; + } + else + { + if (match_pos->type == eTemplateBeg) + { + ++template_scope_depth; + } + else if (match_pos->type == eTemplateEnd) + { + assert(template_scope_depth > 0); + --template_scope_depth; + } + + // Do we have a wildcard going right now? + if (match_wildcard_pos == match_pos_end) + { + // No wildcard matching right now, just check and see if things match + if (*pos == *match_pos) + ++match_count; + else + return false; + } + else + { + // We have a wildcard match going + + // For template types we need to make sure to match the template depths... + const size_t start_wildcard_template_scope_depth = template_scope_depth; + size_t curr_wildcard_template_scope_depth = template_scope_depth; + while (pos != pos_end) + { + if (match_wildcard_pos->type == eNameSpace && pos->type == eParamsBeg) + return false; + + if (start_wildcard_template_scope_depth == curr_wildcard_template_scope_depth) + { + if (*pos == *match_next_pos) + { + ++match_count; + match_pos = match_next_pos; + match_wildcard_pos = match_pos_end; + break; + } + } + if (pos->type == eTemplateBeg) + ++curr_wildcard_template_scope_depth; + else if (pos->type == eTemplateEnd) + --curr_wildcard_template_scope_depth; + + + ++pos; + } + } + } + + if (pos != pos_end) + ++pos; + } + if (match_pos != match_pos_end) + return false; + + return match_count > 0; +} + + +//---------------------------------------------------------------------- +// Return the number of tokens in the token collection +//---------------------------------------------------------------------- +size_t +Mangled::TokenList::Size () const +{ + return m_tokens.size(); +} + + +//---------------------------------------------------------------------- +// Stream out the tokens +//---------------------------------------------------------------------- +Stream& +lldb_private::operator << (Stream& s, const Mangled::TokenList& obj) +{ + obj.Dump(&s); + return s; +} diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp new file mode 100644 index 00000000000..c8e7d2f2c42 --- /dev/null +++ b/lldb/source/Core/Module.cpp @@ -0,0 +1,515 @@ +//===-- 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/Core/Module.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" + +using namespace lldb; +using namespace lldb_private; + +Module::Module(const FileSpec& file_spec, const ArchSpec& arch, const ConstString *object_name, off_t object_offset) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (file_spec.GetModificationTime()), + m_arch (arch), + m_uuid (), + m_file (file_spec), + m_flags (), + m_object_name (), + m_objfile_ap (), + m_symfile_ap () +{ + if (object_name) + m_object_name = *object_name; + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Module::Module((%s) '%s/%s%s%s%s')", + this, + m_arch.AsCString(), + m_file.GetDirectory().AsCString(""), + m_file.GetFilename().AsCString(""), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); + +} + +Module::~Module() +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT); + if (log) + log->Printf ("%p Module::~Module((%s) '%s/%s%s%s%s')", + this, + m_arch.AsCString(), + m_file.GetDirectory().AsCString(""), + m_file.GetFilename().AsCString(""), + m_object_name.IsEmpty() ? "" : "(", + m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), + m_object_name.IsEmpty() ? "" : ")"); +} + + +ModuleSP +Module::GetSP () +{ + return ModuleList::GetModuleSP (this); +} + +const UUID& +Module::GetUUID() +{ + Mutex::Locker locker (m_mutex); + if (m_flags.IsClear(flagsParsedUUID)) + { + ObjectFile * obj_file = GetObjectFile (); + + if (obj_file != NULL) + { + obj_file->GetUUID(&m_uuid); + m_flags.Set(flagsParsedUUID); + } + } + return m_uuid; +} + +void +Module::ParseAllDebugSymbols() +{ + Mutex::Locker locker (m_mutex); + uint32_t num_comp_units = GetNumCompileUnits(); + if (num_comp_units == 0) + return; + + TargetSP null_target; + SymbolContext sc(null_target, GetSP()); + uint32_t cu_idx; + SymbolVendor *symbols = GetSymbolVendor (); + + for (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); + + uint32_t func_idx; + for (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 = GetSP(); +} + +void +Module::DumpSymbolContext(Stream *s) +{ + s->Printf(", Module{0x%8.8x}", this); +} + +uint32_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 (uint32_t index) +{ + Mutex::Locker locker (m_mutex); + uint32_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; +} + +//CompUnitSP +//Module::FindCompUnit(lldb::user_id_t uid) +//{ +// CompUnitSP cu_sp; +// SymbolVendor *symbols = GetSymbolVendor (); +// if (symbols) +// cu_sp = symbols->FindCompUnit(uid); +// 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%llx)", vm_addr); + ObjectFile* ofile = GetObjectFile(); + if (ofile) + return so_addr.ResolveAddressUsingFileSections(vm_addr, ofile->GetSectionList()); + 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 + sc.Clear(); + + // Get the section from the section/offset address. + const Section *section = so_addr.GetSection(); + + // Make sure the section matches this module before we try and match anything + if (section && section->GetModule() == this) + { + // If the section offset based address resolved itself, then this + // is the right module. + sc.module_sp = GetSP(); + 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 + if (resolve_scope & eSymbolContextSymbol) + { + ObjectFile* ofile = GetObjectFile(); + if (ofile) + { + Symtab *symtab = ofile->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); + 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%s%s:%u, check_inlines = %s, resolve_scope = 0x%8.8x)", + file_spec.GetDirectory().AsCString(""), + file_spec.GetDirectory() ? "/" : "", + file_spec.GetFilename().AsCString(""), + 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; +} + + +uint32_t +Module::FindGlobalVariables(const ConstString &name, bool append, uint32_t max_matches, VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(name, append, max_matches, variables); + return 0; +} +uint32_t +Module::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(regex, append, max_matches, variables); + return 0; +} + +uint32_t +Module::FindFunctions(const ConstString &name, bool append, SymbolContextList& sc_list) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindFunctions(name, append, sc_list); + return 0; +} + +uint32_t +Module::FindFunctions(const RegularExpression& regex, bool append, SymbolContextList& sc_list) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindFunctions(regex, append, sc_list); + return 0; +} + +//uint32_t +//Module::FindTypes(const SymbolContext& sc, const ConstString &name, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& types) +//{ +// Timer scoped_timer(__PRETTY_FUNCTION__); +// SymbolVendor *symbols = GetSymbolVendor (); +// if (symbols) +// return symbols->FindTypes(sc, name, append, max_matches, encoding, udt_name, types); +// return 0; +//} +// +//uint32_t +//Module::FindTypes(const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, const char *udt_name, TypeList& types) +//{ +// Timer scoped_timer(__PRETTY_FUNCTION__); +// SymbolVendor *symbols = GetSymbolVendor (); +// if (symbols) +// return symbols->FindTypes(sc, regex, append, max_matches, encoding, udt_name, types); +// return 0; +// +//} + +SymbolVendor* +Module::GetSymbolVendor (bool can_create) +{ + Mutex::Locker locker (m_mutex); + if (m_flags.IsClear(flagsSearchedForSymVendor) && can_create) + { + ObjectFile *obj_file = GetObjectFile (); + if (obj_file != NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + m_symfile_ap.reset(SymbolVendor::FindPlugin(this)); + m_flags.Set (flagsSearchedForSymVendor); + } + } + return m_symfile_ap.get(); +} + +const FileSpec & +Module::GetFileSpec () const +{ + return m_file; +} + +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; +} + +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%s\n", + m_file.GetDirectory().AsCString(), + m_file.GetFilename().AsCString(), + 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_flags.IsClear(flagsSearchedForObjParser)) + { + m_flags.Set (flagsSearchedForObjParser); + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); + m_objfile_ap.reset(ObjectFile::FindPlugin(this, &m_file, 0, m_file.GetByteSize())); + } + return m_objfile_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); + ObjectFile *objfile = GetObjectFile(); + if (objfile) + { + Symtab *symtab = objfile->GetSymtab(); + if (symtab) + return symtab->FindFirstSymbolWithNameAndType (name, symbol_type); + } + 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::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(); + ObjectFile *objfile = GetObjectFile (); + if (objfile) + { + Symtab *symtab = objfile->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(); + ObjectFile *objfile = GetObjectFile (); + if (objfile) + { + Symtab *symtab = objfile->GetSymtab(); + if (symtab) + { + std::vector<uint32_t> symbol_indexes; + symtab->FindAllSymbolsMatchingRexExAndType (regex, symbol_type, symbol_indexes); + SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); + } + } + return sc_list.GetSize() - initial_size; +} + +const TimeValue & +Module::GetModificationTime () const +{ + return m_mod_time; +} diff --git a/lldb/source/Core/ModuleChild.cpp b/lldb/source/Core/ModuleChild.cpp new file mode 100644 index 00000000000..f38fb4f6c36 --- /dev/null +++ b/lldb/source/Core/ModuleChild.cpp @@ -0,0 +1,52 @@ +//===-- 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 (Module* module) : + m_module(module) +{ +} + +ModuleChild::ModuleChild (const ModuleChild& rhs) : + m_module(rhs.m_module) +{ +} + +ModuleChild::~ModuleChild() +{ +} + +const ModuleChild& +ModuleChild::operator= (const ModuleChild& rhs) +{ + if (this != &rhs) + m_module = rhs.m_module; + return *this; +} + +Module * +ModuleChild::GetModule () +{ + return m_module; +} + +Module * +ModuleChild::GetModule () const +{ + return m_module; +} + +void +ModuleChild::SetModule (Module *module) +{ + m_module = module; +} diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp new file mode 100644 index 00000000000..ae6e27b1dd2 --- /dev/null +++ b/lldb/source/Core/ModuleList.cpp @@ -0,0 +1,626 @@ +//===-- 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/Module.h" +#include "lldb/Host/Symbols.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) +{ +} + +//---------------------------------------------------------------------- +// Copy constructor +//---------------------------------------------------------------------- +ModuleList::ModuleList(const ModuleList& rhs) : + m_modules(rhs.m_modules) +{ +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const ModuleList& +ModuleList::operator= (const ModuleList& rhs) +{ + if (this != &rhs) + { + Mutex::Locker locker(m_modules_mutex); + m_modules = rhs.m_modules; + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ModuleList::~ModuleList() +{ +} + +void +ModuleList::Append (ModuleSP &module_sp) +{ + Mutex::Locker locker(m_modules_mutex); + m_modules.push_back(module_sp); +} + +bool +ModuleList::AppendInNeeded (ModuleSP &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. + m_modules.push_back(module_sp); + return true; +} + +bool +ModuleList::Remove (ModuleSP &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); + return true; + } + } + return false; +} + + + +void +ModuleList::Clear() +{ + Mutex::Locker locker(m_modules_mutex); + m_modules.clear(); +} + +Module* +ModuleList::GetModulePointerAtIndex (uint32_t idx) const +{ + Mutex::Locker locker(m_modules_mutex); + if (idx < m_modules.size()) + return m_modules[idx].get(); + return NULL; +} + +ModuleSP +ModuleList::GetModuleAtIndex(uint32_t idx) +{ + Mutex::Locker locker(m_modules_mutex); + ModuleSP module_sp; + if (idx < m_modules.size()) + module_sp = m_modules[idx]; + return module_sp; +} + +size_t +ModuleList::FindFunctions (const ConstString &name, SymbolContextList &sc_list) +{ + 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)->FindFunctions (name, true, sc_list); + } + return sc_list.GetSize(); +} + +uint32_t +ModuleList::FindGlobalVariables (const ConstString &name, bool append, uint32_t max_matches, VariableList& variable_list) +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (name, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +uint32_t +ModuleList::FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variable_list) +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::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) +{ + Mutex::Locker locker(m_modules_mutex); + sc_list.Clear(); + collection::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(); +} + +class ModuleMatches +{ +public: + //-------------------------------------------------------------- + /// Construct with the user ID to look for. + //-------------------------------------------------------------- + ModuleMatches (const FileSpec *file_spec_ptr, + const ArchSpec *arch_ptr, + const UUID *uuid_ptr, + const ConstString *object_name) : + m_file_spec_ptr (file_spec_ptr), + m_arch_ptr (arch_ptr), + m_uuid_ptr (uuid_ptr), + m_object_name (object_name) + { + } + + //-------------------------------------------------------------- + /// Unary predicate function object callback. + //-------------------------------------------------------------- + bool + operator () (const ModuleSP& module_sp) const + { + if (m_file_spec_ptr) + { + if (!FileSpec::Equal (*m_file_spec_ptr, module_sp->GetFileSpec(), m_file_spec_ptr->GetDirectory())) + return false; + } + + if (m_arch_ptr) + { + if (module_sp->GetArchitecture() != *m_arch_ptr) + return false; + } + + if (m_uuid_ptr) + { + if (module_sp->GetUUID() != *m_uuid_ptr) + return false; + } + + if (m_object_name) + { + if (module_sp->GetObjectName() != *m_object_name) + return false; + } + return true; + } + +private: + //-------------------------------------------------------------- + // Member variables. + //-------------------------------------------------------------- + const FileSpec * m_file_spec_ptr; + const ArchSpec * m_arch_ptr; + const UUID * m_uuid_ptr; + const ConstString * m_object_name; +}; + +size_t +ModuleList::FindModules +( + const FileSpec *file_spec_ptr, + const ArchSpec *arch_ptr, + const UUID *uuid_ptr, + const ConstString *object_name, + ModuleList& matching_module_list +) const +{ + size_t existing_matches = matching_module_list.GetSize(); + ModuleMatches matcher (file_spec_ptr, arch_ptr, uuid_ptr, object_name); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator end = m_modules.end(); + collection::const_iterator pos; + + for (pos = std::find_if (m_modules.begin(), end, matcher); + pos != end; + pos = std::find_if (++pos, end, matcher)) + { + ModuleSP module_sp(*pos); + matching_module_list.Append(module_sp); + } + return matching_module_list.GetSize() - existing_matches; +} + +ModuleSP +ModuleList::FindModule (lldb_private::Module *module_ptr) +{ + 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::FindFirstModuleForFileSpec (const FileSpec &file_spec, const ConstString *object_name) +{ + ModuleSP module_sp; + ModuleMatches matcher (&file_spec, NULL, NULL, NULL); + + // Scope for "locker" + { + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator end = m_modules.end(); + collection::const_iterator pos = m_modules.begin(); + + pos = std::find_if (pos, end, matcher); + if (pos != end) + module_sp = (*pos); + } + 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); + } +} + + +bool +ModuleList::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) +{ + 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) +{ + // The address is already section offset so it has a module + uint32_t resolved_flags = 0; + Module *module = so_addr.GetModule(); + if (module) + { + resolved_flags = module->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) +{ + FileSpec file_spec(file_path); + return ResolveSymbolContextsForFileSpec (file_spec, line, check_inlines, resolve_scope, sc_list); +} + +uint32_t +ModuleList::ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + 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(); +} + +uint32_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 () +{ + static ModuleList g_shared_module_list; + return g_shared_module_list; +} + +const lldb::ModuleSP +ModuleList::GetModuleSP (lldb_private::Module *module_ptr) +{ + lldb::ModuleSP module_sp; + if (module_ptr) + { + ModuleList &shared_module_list = GetSharedModuleList (); + module_sp = shared_module_list.FindModule (module_ptr); + assert (module_sp.get()); // This might fire off a few times and we need to make sure it never fires... + } + return module_sp; +} + + +Error +ModuleList::GetSharedModule +( + const FileSpec& in_file_spec, + const ArchSpec& arch, + const UUID *uuid_ptr, + const ConstString *object_name_ptr, + off_t object_offset, + ModuleSP &module_sp, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr +) +{ + ModuleList &shared_module_list = GetSharedModuleList (); + char path[PATH_MAX]; + char uuid_cstr[64]; + + Error error; + + module_sp.reset(); + + if (did_create_ptr) + *did_create_ptr = false; + if (old_module_sp_ptr) + old_module_sp_ptr->reset(); + + + // First just try and get the file where it purports to be (path in + // in_file_spec), then check and uuid. + + if (in_file_spec) + { + // 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. + ModuleList matching_module_list; + Mutex::Locker locker(shared_module_list.m_modules_mutex); + if (shared_module_list.FindModules (&in_file_spec, &arch, uuid_ptr, object_name_ptr, 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 (uuid_ptr == NULL) + { + TimeValue file_spec_mod_time(in_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 (in_file_spec, arch, object_name_ptr, object_offset)); + if (module_sp) + { + // 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.Append(module_sp); + return error; + } + } + } + } + + // 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 (&in_file_spec, arch.IsValid() ? &arch : NULL, uuid_ptr); + + // Don't look for the file if it appears to be the same one we already + // checked for above... + if (file_spec != in_file_spec) + { + if (!file_spec.Exists()) + { + file_spec.GetPath(path, sizeof(path)); + if (file_spec.Exists()) + { + if (uuid_ptr && uuid_ptr->IsValid()) + uuid_ptr->GetAsCString(uuid_cstr, sizeof (uuid_cstr)); + else + uuid_cstr[0] = '\0'; + + + if (arch.IsValid()) + { + if (uuid_cstr[0]) + error.SetErrorStringWithFormat("'%s' does not contain the %s architecture and UUID %s.\n", path, arch.AsCString(), uuid_cstr[0]); + else + error.SetErrorStringWithFormat("'%s' does not contain the %s architecture.\n", path, arch.AsCString()); + } + } + else + { + error.SetErrorStringWithFormat("'%s' does not exist.\n", path); + } + 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. + Mutex::Locker locker(shared_module_list.m_modules_mutex); + ModuleList matching_module_list; + if (shared_module_list.FindModules (&file_spec, &arch, uuid_ptr, object_name_ptr, 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 (uuid_ptr == 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 (file_spec, arch, object_name_ptr, object_offset)); + if (module_sp) + { + if (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.Append(module_sp); + } + else + { + file_spec.GetPath(path, sizeof(path)); + + if (file_spec) + { + if (arch.IsValid()) + error.SetErrorStringWithFormat("Unable to open %s architecture in '%s'.\n", arch.AsCString(), path); + else + error.SetErrorStringWithFormat("Unable to open '%s'.\n", path); + } + else + { + if (uuid_ptr && uuid_ptr->IsValid()) + uuid_ptr->GetAsCString(uuid_cstr, sizeof (uuid_cstr)); + else + uuid_cstr[0] = '\0'; + + if (uuid_cstr[0]) + error.SetErrorStringWithFormat("Cannot locate a module for UUID '%s'.\n", uuid_cstr[0]); + else + error.SetErrorStringWithFormat("Cannot locate a module.\n", path, arch.AsCString()); + } + } + } + } + + return error; +} + diff --git a/lldb/source/Core/Options.cpp b/lldb/source/Core/Options.cpp new file mode 100644 index 00000000000..35a9fa40e1b --- /dev/null +++ b/lldb/source/Core/Options.cpp @@ -0,0 +1,700 @@ +//===-- Options.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/Options.h" + +// C Includes +// C++ Includes +#include <bitset> + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// Options +//------------------------------------------------------------------------- +Options::Options () : + m_getopt_table () +{ +} + +Options::~Options () +{ +} + + +void +Options::ResetOptionValues () +{ + m_seen_options.clear(); +} + +void +Options::OptionSeen (int option_idx) +{ + m_seen_options.insert ((char) option_idx); +} + +// Returns true is set_a is a subset of set_b; Otherwise returns false. + +bool +Options::IsASubset (const OptionSet& set_a, const OptionSet& set_b) +{ + bool is_a_subset = true; + OptionSet::const_iterator pos_a; + OptionSet::const_iterator pos_b; + + // set_a is a subset of set_b if every member of set_a is also a member of set_b + + for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) + { + pos_b = set_b.find(*pos_a); + if (pos_b == set_b.end()) + is_a_subset = false; + } + + return is_a_subset; +} + +// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) && !ElementOf (x, set_b) } + +size_t +Options::OptionsSetDiff (const OptionSet& set_a, const OptionSet& set_b, OptionSet& diffs) +{ + size_t num_diffs = 0; + OptionSet::const_iterator pos_a; + OptionSet::const_iterator pos_b; + + for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) + { + pos_b = set_b.find(*pos_a); + if (pos_b == set_b.end()) + { + ++num_diffs; + diffs.insert(*pos_a); + } + } + + return num_diffs; +} + +// Returns the union of set_a and set_b. Does not put duplicate members into the union. + +void +Options::OptionsSetUnion (const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set) +{ + OptionSet::const_iterator pos; + OptionSet::iterator pos_union; + + // Put all the elements of set_a into the union. + + for (pos = set_a.begin(); pos != set_a.end(); ++pos) + union_set.insert(*pos); + + // Put all the elements of set_b that are not already there into the union. + for (pos = set_b.begin(); pos != set_b.end(); ++pos) + { + pos_union = union_set.find(*pos); + if (pos_union == union_set.end()) + union_set.insert(*pos); + } +} + +bool +Options::VerifyOptions (CommandReturnObject &result) +{ + bool options_are_valid = false; + + int num_levels = m_required_options.size(); + if (num_levels) + { + for (int i = 0; i < num_levels && !options_are_valid; ++i) + { + // This is the correct set of options if: 1). m_seen_options contains all of m_required_options[i] + // (i.e. all the required options at this level are a subset of m_seen_options); AND + // 2). { m_seen_options - m_required_options[i] is a subset of m_options_options[i] (i.e. all the rest of + // m_seen_options are in the set of optional options at this level. + + // Check to see if all of m_required_options[i] are a subset of m_seen_options + if (IsASubset (m_required_options[i], m_seen_options)) + { + // Construct the set difference: remaining_options = {m_seen_options} - {m_required_options[i]} + OptionSet remaining_options; + OptionsSetDiff (m_seen_options, m_required_options[i], remaining_options); + // Check to see if remaining_options is a subset of m_optional_options[i] + if (IsASubset (remaining_options, m_optional_options[i])) + options_are_valid = true; + } + } + } + else + { + options_are_valid = true; + } + + if (options_are_valid) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid combination of options for the given command"); + result.SetStatus (eReturnStatusFailed); + } + + return options_are_valid; +} + +void +Options::BuildValidOptionSets () +{ + // Check to see if we already did this. + if (m_required_options.size() != 0) + return; + + // Check to see if there are any options. + int num_options = NumCommandOptions (); + if (num_options == 0) + return; + + const lldb::OptionDefinition *full_options_table = GetDefinitions(); + uint32_t usage_level = 0; + m_required_options.resize(1); + m_optional_options.resize(1); + + for (int i = 0; i < num_options; ++i) + { + // NOTE: Assumption: The full options table is ordered with usage level growing monotonically. + assert (full_options_table[i].usage_level >= usage_level); + + if (full_options_table[i].usage_level > usage_level) + { + // start a new level + usage_level = full_options_table[i].usage_level; + m_required_options.resize(m_required_options.size()+1); + m_optional_options.resize(m_optional_options.size()+1); + } + else + { + assert (m_required_options.empty() == false); + assert (m_optional_options.empty() == false); + } + + if (full_options_table[i].required) + m_required_options.back().insert(full_options_table[i].short_option); + else + m_optional_options.back().insert(full_options_table[i].short_option); + } +} + +uint32_t +Options::NumCommandOptions () +{ + const lldb::OptionDefinition *full_options_table = GetDefinitions (); + int i = 0; + + if (full_options_table != NULL) + { + while (full_options_table[i].long_option != NULL) + ++i; + } + + return i; +} + +struct option * +Options::GetLongOptions () +{ + // Check to see if this has already been done. + if (m_getopt_table.empty()) + { + // Check to see if there are any options. + const uint32_t num_options = NumCommandOptions(); + if (num_options == 0) + return NULL; + + uint32_t i; + uint32_t j; + const lldb::OptionDefinition *full_options_table = GetDefinitions(); + + std::bitset<256> option_seen; + + m_getopt_table.resize(num_options + 1); + for (i = 0, j = 0; i < num_options; ++i) + { + char short_opt = full_options_table[i].short_option; + + if (option_seen.test(short_opt) == false) + { + m_getopt_table[j].name = full_options_table[i].long_option; + m_getopt_table[j].has_arg = full_options_table[i].option_has_arg; + m_getopt_table[j].flag = NULL; + m_getopt_table[j].val = full_options_table[i].short_option; + option_seen.set(short_opt); + ++j; + } + } + + //getopt_long requires a NULL final entry in the table: + + m_getopt_table[j].name = NULL; + m_getopt_table[j].has_arg = 0; + m_getopt_table[j].flag = NULL; + m_getopt_table[j].val = 0; + } + + return m_getopt_table.data(); +} + + +// This function takes INDENT, which tells how many spaces to output at the front of each line; SPACES, which is +// a string containing 80 spaces; and TEXT, which is the text that is to be output. It outputs the text, on +// multiple lines if necessary, to RESULT, with INDENT spaces at the front of each line. It breaks lines on spaces, +// tabs or newlines, shortening the line if necessary to not break in the middle of a word. It assumes that each +// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters. + + +void +Options::OutputFormattedUsageText +( + Stream &strm, + const char *text, + uint32_t output_max_columns +) +{ + int len = strlen (text); + + // Will it all fit on one line? + + if ((len + strm.GetIndentLevel()) < output_max_columns) + { + // Output it as a single line. + strm.Indent (text); + strm.EOL(); + } + else + { + // We need to break it up into multiple lines. + + int text_width = output_max_columns - strm.GetIndentLevel() - 1; + int start = 0; + int end = start; + int final_end = strlen (text); + int sub_len; + + while (end < final_end) + { + // Don't start the 'text' on a space, since we're already outputting the indentation. + while ((start < final_end) && (text[start] == ' ')) + start++; + + end = start + text_width; + if (end > final_end) + end = final_end; + else + { + // If we're not at the end of the text, make sure we break the line on white space. + while (end > start + && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') + end--; + } + + sub_len = end - start; + if (start != 0) + strm.EOL(); + strm.Indent(); + assert (start < final_end); + assert (start + sub_len <= final_end); + strm.Write(text + start, sub_len); + start = end + 1; + } + strm.EOL(); + } +} + +void +Options::GenerateOptionUsage +( + Stream &strm, + CommandObject *cmd, + const char *program_name) +{ + uint32_t screen_width = 80; + const lldb::OptionDefinition *full_options_table = GetDefinitions(); + const uint32_t save_indent_level = strm.GetIndentLevel(); + const char *name; + + if (cmd) + name = cmd->GetCommandName(); + else + name = program_name; + + strm.PutCString ("\nCommand Options Usage:\n"); + + strm.IndentMore(2); + + // First, show each usage level set of options, e.g. <cmd> [options-for-level-0] + // <cmd> [options-for-level-1] + // etc. + + uint32_t usage_level = 0; + const uint32_t num_options = NumCommandOptions(); + uint32_t i; + for (i = 0; i < num_options; ++i) + { + if (i==0 || full_options_table[i].usage_level > usage_level) + { + // start a new level + usage_level = full_options_table[i].usage_level; + if (usage_level > 0) + { + strm.Printf ("\n"); + } + strm.Indent (name); + } + + // Add current option to the end of out_stream. + + if (full_options_table[i].required) + { + if (full_options_table[i].option_has_arg == required_argument) + { + strm.Printf (" -%c %s", + full_options_table[i].short_option, + full_options_table[i].argument_name); + } + else if (full_options_table[i].option_has_arg == optional_argument) + { + strm.Printf (" -%c [%s]", + full_options_table[i].short_option, + full_options_table[i].argument_name); + } + else + strm.Printf (" -%c", full_options_table[i].short_option); + } + else + { + if (full_options_table[i].option_has_arg == required_argument) + strm.Printf (" [-%c %s]", full_options_table[i].short_option, + full_options_table[i].argument_name); + else if (full_options_table[i].option_has_arg == optional_argument) + strm.Printf (" [-%c [%s]]", full_options_table[i].short_option, + full_options_table[i].argument_name); + else + strm.Printf (" [-%c]", full_options_table[i].short_option); + } + } + + strm.Printf ("\n\n"); + + // Now print out all the detailed information about the various options: long form, short form and help text: + // -- long_name <argument> + // - short <argument> + // help text + + // This variable is used to keep track of which options' info we've printed out, because some options can be in + // more than one usage level, but we only want to print the long form of its information once. + + OptionSet options_seen; + OptionSet::iterator pos; + strm.IndentMore (5); + + int first_option_printed = 1; + for (i = 0; i < num_options; ++i) + { + // Only print out this option if we haven't already seen it. + pos = options_seen.find (full_options_table[i].short_option); + if (pos == options_seen.end()) + { + // Put a newline separation between arguments + if (first_option_printed) + first_option_printed = 0; + else + strm.EOL(); + + options_seen.insert (full_options_table[i].short_option); + strm.Indent (); + strm.Printf ("-%c ", full_options_table[i].short_option); + if (full_options_table[i].argument_name != NULL) + strm.PutCString(full_options_table[i].argument_name); + strm.EOL(); + strm.Indent (); + strm.Printf ("--%s ", full_options_table[i].long_option); + if (full_options_table[i].argument_name != NULL) + strm.PutCString(full_options_table[i].argument_name); + strm.EOL(); + + strm.IndentMore (5); + + if (full_options_table[i].usage_text) + OutputFormattedUsageText (strm, + full_options_table[i].usage_text, + screen_width); + if (full_options_table[i].enum_values != NULL) + { + strm.Indent (); + strm.Printf("Values: "); + for (int j = 0; full_options_table[i].enum_values[j].string_value != NULL; j++) + { + if (j == 0) + strm.Printf("%s", full_options_table[i].enum_values[j].string_value); + else + strm.Printf(" | %s", full_options_table[i].enum_values[j].string_value); + } + strm.EOL(); + } + strm.IndentLess (5); + } + } + + // Restore the indent level + strm.SetIndentLevel (save_indent_level); +} + +// This function is called when we have been given a potentially incomplete set of +// options, such as when an alias has been defined (more options might be added at +// at the time the alias is invoked). We need to verify that the options in the set +// m_seen_options are all part of a set that may be used together, but m_seen_options +// may be missing some of the "required" options. + +bool +Options::VerifyPartialOptions (CommandReturnObject &result) +{ + bool options_are_valid = false; + + int num_levels = m_required_options.size(); + if (num_levels) + { + for (int i = 0; i < num_levels && !options_are_valid; ++i) + { + // In this case we are treating all options as optional rather than required. + // Therefore a set of options is correct if m_seen_options is a subset of the + // union of m_required_options and m_optional_options. + OptionSet union_set; + OptionsSetUnion (m_required_options[i], m_optional_options[i], union_set); + if (IsASubset (m_seen_options, union_set)) + options_are_valid = true; + } + } + + return options_are_valid; +} + +bool +Options::HandleOptionCompletion +( + Args &input, + OptionElementVector &opt_element_vector, + int cursor_index, + int char_pos, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + lldb_private::StringList &matches +) +{ + // For now we just scan the completions to see if the cursor position is in + // an option or its argument. Otherwise we'll call HandleArgumentCompletion. + // In the future we can use completion to validate options as well if we want. + + const OptionDefinition *opt_defs = GetDefinitions(); + + std::string cur_opt_std_str (input.GetArgumentAtIndex(cursor_index)); + cur_opt_std_str.erase(char_pos); + const char *cur_opt_str = cur_opt_std_str.c_str(); + + for (int i = 0; i < opt_element_vector.size(); i++) + { + int opt_pos = opt_element_vector[i].opt_pos; + int opt_arg_pos = opt_element_vector[i].opt_arg_pos; + int opt_defs_index = opt_element_vector[i].opt_defs_index; + if (opt_pos == cursor_index) + { + // We're completing the option itself. + if (opt_defs_index != -1) + { + // We recognized it, if it an incomplete long option, complete it anyway (getopt_long is + // happy with shortest unique string, but it's still a nice thing to do.) Otherwise return + // The string so the upper level code will know this is a full match and add the " ". + if (cur_opt_str && strlen (cur_opt_str) > 2 + && cur_opt_str[0] == '-' && cur_opt_str[1] == '-' + && strcmp (opt_defs[opt_defs_index].long_option, cur_opt_str) != 0) + { + std::string full_name ("--"); + full_name.append (opt_defs[opt_defs_index].long_option); + matches.AppendString(full_name.c_str()); + return true; + } + else + { + matches.AppendString(input.GetArgumentAtIndex(cursor_index)); + return true; + } + } + else + { + // FIXME - not handling wrong options yet: + // Check to see if they are writing a long option & complete it. + // I think we will only get in here if the long option table has two elements + // that are not unique up to this point. getopt_long does shortest unique match + // for long options already. + + if (cur_opt_str && strlen (cur_opt_str) > 2 + && cur_opt_str[0] == '-' && cur_opt_str[1] == '-') + { + for (int i = 0 ; opt_defs[i].short_option != 0 ; i++) + { + if (strstr(opt_defs[i].long_option, cur_opt_str + 2) == opt_defs[i].long_option) + { + std::string full_name ("--"); + full_name.append (opt_defs[i].long_option); + // The options definitions table has duplicates because of the + // way the grouping information is stored, so only add once. + bool duplicate = false; + for (int j = 0; j < matches.GetSize(); j++) + { + if (matches.GetStringAtIndex(j) == full_name) + { + duplicate = true; + break; + } + } + if (!duplicate) + matches.AppendString(full_name.c_str()); + } + } + } + return true; + } + + + } + else if (opt_arg_pos == cursor_index) + { + // Okay the cursor is on the completion of an argument. + // See if it has a completion, otherwise return no matches. + + if (opt_defs_index != -1) + { + HandleOptionArgumentCompletion (input, + cursor_index, + strlen (input.GetArgumentAtIndex(cursor_index)), + opt_element_vector, + i, + match_start_point, + max_return_elements, + interpreter, + matches); + return true; + } + else + { + // No completion callback means no completions... + return true; + } + + } + else + { + // Not the last element, keep going. + continue; + } + } + return false; +} + +bool +Options::HandleOptionArgumentCompletion +( + Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + lldb_private::CommandInterpreter *interpreter, + lldb_private::StringList &matches +) +{ + const OptionDefinition *opt_defs = GetDefinitions(); + std::auto_ptr<SearchFilter> filter_ap; + + int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; + int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; + + // See if this is an enumeration type option, and if so complete it here: + + OptionEnumValueElement *enum_values = opt_defs[opt_defs_index].enum_values; + if (enum_values != NULL) + { + bool return_value = false; + std::string match_string(input.GetArgumentAtIndex (opt_arg_pos), input.GetArgumentAtIndex (opt_arg_pos) + char_pos); + for (int i = 0; enum_values[i].string_value != NULL; i++) + { + if (strstr(enum_values[i].string_value, match_string.c_str()) == enum_values[i].string_value) + { + matches.AppendString (enum_values[i].string_value); + return_value = true; + } + } + return return_value; + } + + // If this is a source file or symbol type completion, and there is a + // -shlib option somewhere in the supplied arguments, then make a search filter + // for that shared library. + // FIXME: Do we want to also have an "OptionType" so we don't have to match string names? + + uint32_t completion_mask = opt_defs[opt_defs_index].completionType; + if (completion_mask & CommandCompletions::eSourceFileCompletion + || completion_mask & CommandCompletions::eSymbolCompletion) + { + for (int i = 0; i < opt_element_vector.size(); i++) + { + int cur_defs_index = opt_element_vector[i].opt_defs_index; + int cur_arg_pos = opt_element_vector[i].opt_arg_pos; + const char *cur_opt_name = opt_defs[cur_defs_index].long_option; + + // If this is the "shlib" option and there was an argument provided, + // restrict it to that shared library. + if (strcmp(cur_opt_name, "shlib") == 0 && cur_arg_pos != -1) + { + const char *module_name = input.GetArgumentAtIndex(cur_arg_pos); + if (module_name) + { + FileSpec module_spec(module_name); + lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP(); + // Search filters require a target... + if (target_sp != NULL) + filter_ap.reset (new SearchFilterByModule (target_sp, module_spec)); + } + break; + } + } + } + + return CommandCompletions::InvokeCommonCompletionCallbacks (completion_mask, + input.GetArgumentAtIndex (opt_arg_pos), + match_start_point, + max_return_elements, + interpreter, + filter_ap.get(), + matches); + +} diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp new file mode 100644 index 00000000000..3723e7cf89a --- /dev/null +++ b/lldb/source/Core/PluginManager.cpp @@ -0,0 +1,1133 @@ +//===-- 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/Core/PluginManager.h" + +#include <string> +#include <vector> + +using namespace lldb_private; + +typedef enum PluginAction +{ + ePluginRegisterInstance, + ePluginUnregisterInstance, + ePluginGetInstanceAtIndex +}; + + +#pragma mark ABI + + +typedef struct ABIInstance +{ + ABIInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ABICreateInstance create_callback; +}; + +typedef std::vector<ABIInstance> ABIInstances; + +static bool +AccessABIInstances (PluginAction action, ABIInstance &instance, uint32_t index) +{ + static ABIInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ABIInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin + ( + const char *name, + const char *description, + ABICreateInstance create_callback + ) +{ + if (create_callback) + { + ABIInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessABIInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ABICreateInstance create_callback) +{ + if (create_callback) + { + ABIInstance instance; + instance.create_callback = create_callback; + return AccessABIInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackAtIndex (uint32_t idx) +{ + ABIInstance instance; + if (AccessABIInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ABIInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessABIInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + +#pragma mark Disassembler + + +typedef struct DisassemblerInstance +{ + DisassemblerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + DisassemblerCreateInstance create_callback; +}; + +typedef std::vector<DisassemblerInstance> DisassemblerInstances; + +static bool +AccessDisassemblerInstances (PluginAction action, DisassemblerInstance &instance, uint32_t index) +{ + static DisassemblerInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + DisassemblerInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + DisassemblerCreateInstance create_callback +) +{ + if (create_callback) + { + DisassemblerInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessDisassemblerInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DisassemblerCreateInstance create_callback) +{ + if (create_callback) + { + DisassemblerInstance instance; + instance.create_callback = create_callback; + return AccessDisassemblerInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackAtIndex (uint32_t idx) +{ + DisassemblerInstance instance; + if (AccessDisassemblerInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + DisassemblerInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessDisassemblerInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + +#pragma mark DynamicLoader + + +typedef struct DynamicLoaderInstance +{ + DynamicLoaderInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + DynamicLoaderCreateInstance create_callback; +}; + +typedef std::vector<DynamicLoaderInstance> DynamicLoaderInstances; + +static bool +AccessDynamicLoaderInstances (PluginAction action, DynamicLoaderInstance &instance, uint32_t index) +{ + static DynamicLoaderInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + DynamicLoaderInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + DynamicLoaderCreateInstance create_callback +) +{ + if (create_callback) + { + DynamicLoaderInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessDynamicLoaderInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DynamicLoaderCreateInstance create_callback) +{ + if (create_callback) + { + DynamicLoaderInstance instance; + instance.create_callback = create_callback; + return AccessDynamicLoaderInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx) +{ + DynamicLoaderInstance instance; + if (AccessDynamicLoaderInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + DynamicLoaderInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessDynamicLoaderInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + + +#pragma mark ObjectFile + +typedef struct ObjectFileInstance +{ + ObjectFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ObjectFileCreateInstance create_callback; +}; + +typedef std::vector<ObjectFileInstance> ObjectFileInstances; + +static bool +AccessObjectFileInstances (PluginAction action, ObjectFileInstance &instance, uint32_t index) +{ + static ObjectFileInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ObjectFileInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + ObjectFileCreateInstance create_callback +) +{ + if (create_callback) + { + ObjectFileInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessObjectFileInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectFileCreateInstance create_callback) +{ + if (create_callback) + { + ObjectFileInstance instance; + instance.create_callback = create_callback; + return AccessObjectFileInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackAtIndex (uint32_t idx) +{ + ObjectFileInstance instance; + if (AccessObjectFileInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ObjectFileInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessObjectFileInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + +#pragma mark ObjectContainer + +typedef struct ObjectContainerInstance +{ + ObjectContainerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ObjectContainerCreateInstance create_callback; +}; + +typedef std::vector<ObjectContainerInstance> ObjectContainerInstances; + +static bool +AccessObjectContainerInstances (PluginAction action, ObjectContainerInstance &instance, uint32_t index) +{ + static ObjectContainerInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ObjectContainerInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + ObjectContainerCreateInstance create_callback +) +{ + if (create_callback) + { + ObjectContainerInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessObjectContainerInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectContainerCreateInstance create_callback) +{ + if (create_callback) + { + ObjectContainerInstance instance; + instance.create_callback = create_callback; + return AccessObjectContainerInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackAtIndex (uint32_t idx) +{ + ObjectContainerInstance instance; + if (AccessObjectContainerInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ObjectContainerInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessObjectContainerInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + +#pragma mark LogChannel + +typedef struct LogChannelInstance +{ + LogChannelInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + LogChannelCreateInstance create_callback; +}; + +typedef std::vector<LogChannelInstance> LogChannelInstances; + +static bool +AccessLogChannelInstances (PluginAction action, LogChannelInstance &instance, uint32_t index) +{ + static LogChannelInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + LogChannelInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + LogChannelCreateInstance create_callback +) +{ + if (create_callback) + { + LogChannelInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessLogChannelInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LogChannelCreateInstance create_callback) +{ + if (create_callback) + { + LogChannelInstance instance; + instance.create_callback = create_callback; + return AccessLogChannelInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +const char * +PluginManager::GetLogChannelCreateNameAtIndex (uint32_t idx) +{ + LogChannelInstance instance; + if (AccessLogChannelInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.name.c_str(); + return NULL; +} + + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackAtIndex (uint32_t idx) +{ + LogChannelInstance instance; + if (AccessLogChannelInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + LogChannelInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessLogChannelInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + +#pragma mark Process + +typedef struct ProcessInstance +{ + ProcessInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + ProcessCreateInstance create_callback; +}; + +typedef std::vector<ProcessInstance> ProcessInstances; + +static bool +AccessProcessInstances (PluginAction action, ProcessInstance &instance, uint32_t index) +{ + static ProcessInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + ProcessInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + ProcessCreateInstance create_callback +) +{ + if (create_callback) + { + ProcessInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessProcessInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ProcessCreateInstance create_callback) +{ + if (create_callback) + { + ProcessInstance instance; + instance.create_callback = create_callback; + return AccessProcessInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackAtIndex (uint32_t idx) +{ + ProcessInstance instance; + if (AccessProcessInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + ProcessInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessProcessInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + +#pragma mark SymbolFile + +typedef struct SymbolFileInstance +{ + SymbolFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + SymbolFileCreateInstance create_callback; +}; + +typedef std::vector<SymbolFileInstance> SymbolFileInstances; + +static bool +AccessSymbolFileInstances (PluginAction action, SymbolFileInstance &instance, uint32_t index) +{ + static SymbolFileInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + SymbolFileInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + SymbolFileCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolFileInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessSymbolFileInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolFileCreateInstance create_callback) +{ + if (create_callback) + { + SymbolFileInstance instance; + instance.create_callback = create_callback; + return AccessSymbolFileInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackAtIndex (uint32_t idx) +{ + SymbolFileInstance instance; + if (AccessSymbolFileInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + SymbolFileInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessSymbolFileInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + + +#pragma mark SymbolVendor + +typedef struct SymbolVendorInstance +{ + SymbolVendorInstance() : + name(), + description(), + create_callback(NULL) + { + } + + std::string name; + std::string description; + SymbolVendorCreateInstance create_callback; +}; + +typedef std::vector<SymbolVendorInstance> SymbolVendorInstances; + +static bool +AccessSymbolVendorInstances (PluginAction action, SymbolVendorInstance &instance, uint32_t index) +{ + static SymbolVendorInstances g_plugin_instances; + + switch (action) + { + case ePluginRegisterInstance: + if (instance.create_callback) + { + g_plugin_instances.push_back (instance); + return true; + } + break; + + case ePluginUnregisterInstance: + if (instance.create_callback) + { + SymbolVendorInstances::iterator pos, end = g_plugin_instances.end(); + for (pos = g_plugin_instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == instance.create_callback) + { + g_plugin_instances.erase(pos); + return true; + } + } + } + break; + + case ePluginGetInstanceAtIndex: + if (index < g_plugin_instances.size()) + { + instance = g_plugin_instances[index]; + return true; + } + break; + + default: + break; + } + return false; +} + +bool +PluginManager::RegisterPlugin +( + const char *name, + const char *description, + SymbolVendorCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolVendorInstance instance; + assert (name && name[0]); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + return AccessSymbolVendorInstances (ePluginRegisterInstance, instance, 0); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolVendorCreateInstance create_callback) +{ + if (create_callback) + { + SymbolVendorInstance instance; + instance.create_callback = create_callback; + return AccessSymbolVendorInstances (ePluginUnregisterInstance, instance, 0); + } + return false; +} + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackAtIndex (uint32_t idx) +{ + SymbolVendorInstance instance; + if (AccessSymbolVendorInstances (ePluginGetInstanceAtIndex, instance, idx)) + return instance.create_callback; + return false; +} + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackForPluginName (const char *name) +{ + if (name && name[0]) + { + SymbolVendorInstance instance; + std::string ss_name(name); + for (uint32_t idx = 0; AccessSymbolVendorInstances (ePluginGetInstanceAtIndex, instance, idx); ++idx) + { + if (instance.name == ss_name) + return instance.create_callback; + } + } + return NULL; +} + + diff --git a/lldb/source/Core/RegularExpression.cpp b/lldb/source/Core/RegularExpression.cpp new file mode 100644 index 00000000000..aca1671c181 --- /dev/null +++ b/lldb/source/Core/RegularExpression.cpp @@ -0,0 +1,154 @@ +//===-- 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" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +RegularExpression::RegularExpression() : + m_re(), + m_comp_err (1), + m_preg(), + m_matches() +{ + 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() +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re, flags); +} + +//---------------------------------------------------------------------- +// 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, int flags) +{ + Free(); + 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, size_t num_matches, int execute_flags) const +{ + int match_result = 1; + if (m_comp_err == 0) + { + if (num_matches > 0) + m_matches.resize(num_matches + 1); + else + m_matches.clear(); + + match_result = ::regexec (&m_preg, + s, + m_matches.size(), + const_cast<regmatch_t *>(m_matches.data()), + execute_flags); + } + return match_result == 0; +} + +bool +RegularExpression::GetMatchAtIndex (const char* s, uint32_t idx, std::string& match_str) const +{ + if (idx <= m_preg.re_nsub && idx < m_matches.size()) + { + match_str.assign (s + m_matches[idx].rm_so, + m_matches[idx].rm_eo - m_matches[idx].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 +{ + 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; + } +} diff --git a/lldb/source/Core/Scalar.cpp b/lldb/source/Core/Scalar.cpp new file mode 100644 index 00000000000..56590b8d69e --- /dev/null +++ b/lldb/source/Core/Scalar.cpp @@ -0,0 +1,2084 @@ +//===-- 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 "lldb/Core/Args.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/DataExtractor.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 (eByteOrderHost == 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, eByteOrderHost); + } + else if (eByteOrderHost == 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, eByteOrderHost); + } + } + else + { + // We want all of the data + data.SetData((uint8_t*)&m_data, byte_size, eByteOrderHost); + } + return true; + } + data.Clear(); + return false; +} + +size_t +Scalar::GetByteSize() const +{ + switch (m_type) + { + default: + 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) + { + default: + 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: + default: + 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) + { + default: + break; + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: break; + 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(int)) + return e_sint; + if (byte_size <= sizeof(long)) + return e_slong; + if (byte_size <= sizeof(long long)) + return e_slonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForUnsignedIntegerWithByteSize (size_t byte_size) +{ + if (byte_size <= sizeof(unsigned int)) + return e_uint; + if (byte_size <= sizeof(unsigned long)) + return e_ulong; + if (byte_size <= sizeof(unsigned long long)) + return e_ulonglong; + return e_void; +} + +Scalar::Type +Scalar::GetValueTypeForFloatWithByteSize (size_t byte_size) +{ + if (byte_size == sizeof(float)) + return e_float; + if (byte_size == sizeof(double)) + return e_double; + if (byte_size == sizeof(long double)) + 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) + { + default: + 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) + { + default: + 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.slong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.ulong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.slonglong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.ulonglong; success = true; break; + case e_uint: m_data.uint = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.flt; success = true; break; + case e_uint: m_data.uint = m_data.flt; success = true; break; + case e_slong: m_data.slong = m_data.flt; success = true; break; + case e_ulong: m_data.ulong = m_data.flt; success = true; break; + case e_slonglong: m_data.slonglong = m_data.flt; success = true; break; + case e_ulonglong: m_data.ulonglong = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.dbl; success = true; break; + case e_uint: m_data.uint = m_data.dbl; success = true; break; + case e_slong: m_data.slong = m_data.dbl; success = true; break; + case e_ulong: m_data.ulong = m_data.dbl; success = true; break; + case e_slonglong: m_data.slonglong = m_data.dbl; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.dbl; success = true; break; + case e_float: m_data.flt = 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) + { + default: + case e_void: + case e_sint: m_data.sint = m_data.ldbl; success = true; break; + case e_uint: m_data.uint = m_data.ldbl; success = true; break; + case e_slong: m_data.slong = m_data.ldbl; success = true; break; + case e_ulong: m_data.ulong = m_data.ldbl; success = true; break; + case e_slonglong: m_data.slonglong = m_data.ldbl; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ldbl; success = true; break; + case e_float: m_data.flt = m_data.ldbl; success = true; break; + case e_double: m_data.dbl = m_data.ldbl; success = true; break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +int +Scalar::SInt(int fail_value) const +{ + switch (m_type) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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(int)) + return m_data.uint; + else if (sizeof(m_data.flt) == sizeof(unsigned long)) + return m_data.ulong; + else if (sizeof(m_data.flt) == sizeof(unsigned long long)) + return m_data.ulonglong; + break; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(int)) + return m_data.uint; + else if (sizeof(m_data.dbl) == sizeof(unsigned long)) + return m_data.ulong; + else if (sizeof(m_data.dbl) == sizeof(unsigned long long)) + return m_data.ulonglong; + break; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(int)) + return m_data.uint; + else if (sizeof(m_data.ldbl) == sizeof(unsigned long)) + return m_data.ulong; + else if (sizeof(m_data.ldbl) == sizeof(unsigned long long)) + return m_data.ulonglong; + break; + } + return fail_value; +} + + + +long long +Scalar::SLongLong(long long fail_value) const +{ + switch (m_type) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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; + + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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) + { + default: + 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; + + default: + 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; + + default: + 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; + + default: + 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; + + default: + 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; +} + +// 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, uint32_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) + { + default: + 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.\n", value_str); + else if (!UIntValueIsValidForSize (uval64, byte_size)) + error.SetErrorStringWithFormat ("Value 0x%llx is too large to fit in a %u byte unsigned integer value.\n", uval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_uint: m_data.uint = uval64; break; + case e_ulong: m_data.ulong = uval64; break; + case e_ulonglong: m_data.ulonglong = uval64; break; + default: + error.SetErrorStringWithFormat ("Unsupported unsigned integer byte size: %u.\n", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("Unsupported unsigned integer byte size: %u.\n", 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.\n", value_str); + else if (!SIntValueIsValidForSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("Value 0x%llx is too large to fit in a %u byte signed integer value.\n", sval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_sint: m_data.sint = sval64; break; + case e_slong: m_data.slong = sval64; break; + case e_slonglong: m_data.slonglong = sval64; break; + default: + error.SetErrorStringWithFormat ("Unsupported signed integer byte size: %u.\n", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("Unsupported signed integer byte size: %u.\n", 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.\n", 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.\n", 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.\n", value_str); + } + else + { + error.SetErrorStringWithFormat ("Unsupported float byte size: %u.\n", byte_size); + return error; + } + break; + + case eEncodingVector: + error.SetErrorString ("Vector encoding unsupported."); + break; + } + if (error.Fail()) + m_type = e_void; + + return error; +} + +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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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)) + { + default: + 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/lldb/source/Core/SearchFilter.cpp b/lldb/source/Core/SearchFilter.cpp new file mode 100644 index 00000000000..4c54a912801 --- /dev/null +++ b/lldb/source/Core/SearchFilter.cpp @@ -0,0 +1,435 @@ +//===-- 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/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(lldb::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::SymbolContextPasses +( + const SymbolContext &context, + lldb::SymbolContextItem scope +) +{ + return true; +} + +bool +SearchFilter::AddressPasses (Address &address) +{ + return true; +} + +bool +SearchFilter::CompUnitPasses (FileSpec &fileSpec) +{ + return true; +} + +bool +SearchFilter::CompUnitPasses (CompileUnit &compUnit) +{ + return true; +} + +void +SearchFilter::GetDescription (Stream *s) +{ + s->PutCString("No Filter"); +} + +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 == NULL) + 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 == NULL) + return; + empty_sc.target_sp = m_target_sp; + + if (searcher.GetDepth() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + else + { + size_t numModules = modules.GetSize(); + + for (int i = 0; i < numModules; i++) + { + ModuleSP module_sp(modules.GetModuleAtIndex(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) +{ + Searcher::CallbackReturn shouldContinue; + + if (searcher.GetDepth () >= Searcher::eDepthModule) + { + if (!context.module_sp) + { + size_t n_modules = m_target_sp->GetImages().GetSize(); + for (int i = 0; i < n_modules; i++) + { + // If this is the last level supplied, then call the callback directly, + // otherwise descend. + ModuleSP module_sp(m_target_sp->GetImages().GetModuleAtIndex(i)); + if (!ModulePasses (module_sp)) + continue; + + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(m_target_sp, module_sp); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + if (shouldContinue == Searcher::eCallbackReturnStop + || shouldContinue == Searcher::eCallbackReturnPop) + return shouldContinue; + } + else + { + shouldContinue = DoCUIteration(module_sp, context, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return shouldContinue; + else if (shouldContinue == Searcher::eCallbackReturnPop) + continue; + } + } + } + else + { + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(context.module_sp.get()); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + } + else + { + return DoCUIteration(context.module_sp, context, searcher); + } + } + + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::CallbackReturn +SearchFilter::DoCUIteration (const ModuleSP &module_sp, const SymbolContext &context, Searcher &searcher) +{ + Searcher::CallbackReturn shouldContinue; + if (context.comp_unit == NULL) + { + uint32_t num_comp_units = module_sp->GetNumCompileUnits(); + for (uint32_t i = 0; i < num_comp_units; i++) + { + CompUnitSP cu_sp (module_sp->GetCompileUnitAtIndex (i)); + 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; +} + +//---------------------------------------------------------------------- +// SearchFilterByModule: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModule constructors +//---------------------------------------------------------------------- + +SearchFilterByModule::SearchFilterByModule (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::Compare (module_sp->GetFileSpec(), m_module_spec, false) == 0) + return true; + else + return false; +} + +bool +SearchFilterByModule::ModulePasses (const FileSpec &spec) +{ + if (FileSpec::Compare(spec, m_module_spec, false) == 0) + return true; + else + return false; +} + +bool +SearchFilterByModule::SymbolContextPasses +( + const SymbolContext &context, + lldb::SymbolContextItem scope + ) +{ + if (!(scope & eSymbolContextModule)) + return false; + + if (context.module_sp && FileSpec::Compare (context.module_sp->GetFileSpec(), m_module_spec, false) == 0) + return true; + else + return false; +} + +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. + + ModuleList matching_modules; + // const size_t num_matching_modules = m_target_sp->GetImages().FindModules(&m_module_spec, NULL, NULL, NULL, matching_modules); + for (int i = 0; i < m_target_sp->GetImages().GetSize (); i++) + { + Module* module = m_target_sp->GetImages().GetModulePointerAtIndex(i); + if (FileSpec::Compare (m_module_spec, module->GetFileSpec(), false) == 0) + { + SymbolContext matchingContext(m_target_sp, module->GetSP()); + Searcher::CallbackReturn shouldContinue; + + shouldContinue = DoModuleIteration(matchingContext, searcher); + if (shouldContinue == Searcher::eCallbackReturnStop) + return; + } + } +} + +void +SearchFilterByModule::GetDescription (Stream *s) +{ + s->PutCString("In 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>")); + } +} + +void +SearchFilterByModule::Dump (Stream *s) const +{ + +} diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp new file mode 100644 index 00000000000..57c519918f2 --- /dev/null +++ b/lldb/source/Core/Section.cpp @@ -0,0 +1,791 @@ +//===-- 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/Process.h" + +using namespace lldb; +using namespace lldb_private; + +Section::Section +( + Section *parent, + Module* module, + user_id_t sect_id, + const ConstString &name, + SectionType sect_type, + addr_t file_addr, + addr_t byte_size, + uint64_t file_offset, + uint64_t file_size, + uint32_t flags +) : + ModuleChild (module), + UserID (sect_id), + Flags (flags), + m_parent (parent), + m_name (name), + m_type (sect_type), + 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_linked_section(NULL), + m_linked_offset (0) +{ +} + +//Section::Section +//( +// Section *parent, +// Module* module, +// user_id_t sect_id, +// const ConstString &name, +// const AddressRange *file_vm_range, +// uint64_t file_offset, +// uint64_t file_size, +// uint32_t flags +//) : +// ModuleChild (module), +// UserID (sect_id), +// Flags (flags), +// m_parent (parent), +// m_name (name), +// m_range (), +// m_file_offset (file_offset), +// m_file_size (file_size), +// m_children (), +// m_fake (false) +//{ +// if (file_vm_range) +// m_range = *file_vm_range; +//} + +Section::~Section() +{ +} + + +// Get a valid shared pointer to this section object +SectionSP +Section::GetSharedPointer() const +{ + SectionSP this_sp; + if (m_parent) + this_sp = m_parent->GetChildren().GetSharedPointer (this, false); + else + { + ObjectFile *objfile = m_module->GetObjectFile(); + if (objfile) + { + SectionList *section_list = objfile->GetSectionList(); + if (section_list) + this_sp = section_list->GetSharedPointer (this, false); + } + } + return this_sp; +} + + + +ConstString& +Section::GetName() +{ + if (m_linked_section) + return const_cast<Section *>(m_linked_section)->GetName(); + return m_name; +} + +const ConstString& +Section::GetName() const +{ + if (m_linked_section) + return m_linked_section->GetName(); + return m_name; +} + +SectionList& +Section::GetChildren() +{ + return m_children; +} + +const SectionList& +Section::GetChildren() const +{ + return m_children; +} + +addr_t +Section::GetFileAddress () const +{ + if (m_parent) + { + // 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 m_parent->GetFileAddress() + m_file_addr; + } + // This section has no parent, so m_file_addr is the file base address + return m_file_addr; +} + +addr_t +Section::GetLinkedFileAddress () const +{ + if (m_linked_section) + return m_linked_section->GetFileAddress() + m_linked_offset; + return LLDB_INVALID_ADDRESS; +} + +addr_t +Section::GetOffset () const +{ + if (m_parent) + { + // This section has a parent which means m_file_addr is an offset. + return m_file_addr; + } + + // This section has no parent, so there is no offset to be had + return 0; +} + +addr_t +Section::GetByteSize () const +{ + return m_byte_size; +} + +void +Section::SetByteSize (addr_t byte_size) +{ + m_byte_size = byte_size; +} + + +addr_t +Section::GetLoadBaseAddress (Process *process) const +{ + addr_t load_base_addr = LLDB_INVALID_ADDRESS; + if (m_linked_section) + { + load_base_addr = m_linked_section->GetLoadBaseAddress(process) + m_linked_offset; + } + else + if (m_parent) + { + load_base_addr = m_parent->GetLoadBaseAddress (process); + if (load_base_addr != LLDB_INVALID_ADDRESS) + load_base_addr += GetOffset(); + } + else + { + load_base_addr = process->GetSectionLoadAddress(this); + } + + return load_base_addr; +} + +bool +Section::ResolveContainedAddress (addr_t offset, Address &so_addr) const +{ + const uint32_t num_children = m_children.GetSize(); + if (num_children > 0) + { + for (uint32_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); + } + } + if (m_linked_section) + { + so_addr.SetOffset(m_linked_offset + offset); + so_addr.SetSection(m_linked_section); + } + else + { + so_addr.SetOffset(offset); + so_addr.SetSection(this); + } + return true; +} + +uint64_t +Section::GetFileOffset() const +{ + return m_file_offset; +} + +uint64_t +Section::GetFileSize() const +{ + return m_file_size; +} + +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; +} + +bool +Section::ContainsLinkedFileAddress (addr_t vm_addr) const +{ + const addr_t linked_file_addr = GetLinkedFileAddress(); + if (linked_file_addr != LLDB_INVALID_ADDRESS) + { + if (linked_file_addr <= vm_addr) + { + const addr_t offset = vm_addr - linked_file_addr; + return offset < GetByteSize(); + } + } + return false; +} + +int +Section::Compare (const Section& a, const Section& b) +{ + if (&a == &b) + return 0; + + const Module* a_module = a.GetModule(); + const Module* b_module = b.GetModule(); + if (a_module == b_module) + { + 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 < b_module) + return -1; + else + return 1; // We already know the modules aren't equal + } +} + + +void +Section::Dump(Stream *s, Process *process) const +{ + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->Printf("0x%8.8x ", GetID()); + bool resolved = true; + addr_t addr = LLDB_INVALID_ADDRESS; + + if (GetByteSize() == 0) + s->Printf("%39s", ""); + else + { + if (process) + addr = GetLoadBaseAddress (process); + + if (addr == LLDB_INVALID_ADDRESS) + { + if (process) + resolved = false; + addr = GetFileAddress(); + } + + VMRange range(addr, addr + m_byte_size); + range.Dump (s, 0); + } + + s->Printf("%c 0x%8.8llx 0x%8.8llx 0x%8.8x ", resolved ? ' ' : '*', m_file_offset, m_file_size, GetAllFlagBits()); + + DumpName (s); + + s->EOL(); + + if (m_linked_section) + { + addr = LLDB_INVALID_ADDRESS; + + if (process) + { + addr = m_linked_section->GetLoadBaseAddress(process); + if (addr != LLDB_INVALID_ADDRESS) + addr += m_linked_offset; + } + + if (addr == LLDB_INVALID_ADDRESS) + { + if (process) + resolved = false; + addr = m_linked_section->GetFileAddress() + m_linked_offset; + } + + int indent = (sizeof(void*) + 1 + sizeof(user_id_t) + 1) * 2 + 3 + s->GetIndentLevel(); + s->Printf("%*.*s", indent, indent, ""); + VMRange linked_range(addr, addr + m_byte_size); + linked_range.Dump (s, 0); + indent = 3 * (sizeof(uint32_t) * 2 + 2 + 1) + 1; + s->Printf("%c%*.*s", resolved ? ' ' : '*', indent, indent, ""); + + m_linked_section->DumpName(s); + s->Printf(" + 0x%llx\n", m_linked_offset); + } + + m_children.Dump(s, process, false); +} + +void +Section::DumpName (Stream *s) const +{ + if (m_linked_section) + return m_linked_section->DumpName(s); + else if (m_parent == NULL) + { + // The top most section prints the module basename + const char *module_basename = m_module->GetFileSpec().GetFilename().AsCString(); + if (module_basename && module_basename[0]) + s->Printf("%s.", module_basename); + } + else + { + m_parent->DumpName (s); + s->PutChar('.'); + } + m_name.Dump(s); +} + +//---------------------------------------------------------------------- +// Get the section data from a complete contiguous copy of the +// entire executable image. +//---------------------------------------------------------------------- +size_t +Section::GetSectionDataFromImage (const DataExtractor& image_data, DataExtractor& section_data) const +{ + size_t file_size = GetByteSize(); + if (file_size > 0) + { + off_t file_offset = GetFileOffset(); + if (section_data.SetData (image_data, file_offset, file_size) == file_size) + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// Get the section data the file on disk +//---------------------------------------------------------------------- +size_t +Section::ReadSectionDataFromObjectFile(const ObjectFile* objfile, DataExtractor& section_data) const +{ + if (objfile == NULL) + return 0; + + const FileSpec& file = objfile->GetFileSpec(); + + if (file) + { + size_t section_file_size = GetByteSize(); + if (section_file_size > 0) + { + off_t section_file_offset = GetFileOffset() + objfile->GetOffset(); + DataBufferSP sectionDataSP(file.ReadFileContents(section_file_offset, section_file_size)); + + section_data.SetByteOrder(objfile->GetByteOrder()); + section_data.SetAddressByteSize(objfile->GetAddressByteSize()); + return section_data.SetData (sectionDataSP); + } + } + return 0; +} + +size_t +Section::MemoryMapSectionDataFromObjectFile(const ObjectFile* objfile, DataExtractor& section_data) const +{ + if (objfile == NULL) + return 0; + + const FileSpec& file = objfile->GetFileSpec(); + + if (file) + { + size_t section_file_size = GetFileSize(); + if (section_file_size > 0) + { + off_t section_file_offset = GetFileOffset() + objfile->GetOffset(); + DataBufferSP sectionDataSP(file.MemoryMapFileContents(section_file_offset, section_file_size)); + section_data.SetByteOrder(objfile->GetByteOrder()); + section_data.SetAddressByteSize(objfile->GetAddressByteSize()); + return section_data.SetData (sectionDataSP); + } + } + return 0; +} + +bool +Section::IsFake() const +{ + return m_fake; +} + +void +Section::SetIsFake(bool fake) +{ + m_fake = fake; +} + + +bool +Section::IsDescendant (const Section *section) +{ + if (this == section) + return true; + if (m_parent) + return m_parent->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; +} + +void +Section::SetLinkedLocation (const Section *linked_section, uint64_t linked_offset) +{ + if (linked_section) + m_module = linked_section->GetModule(); + m_linked_section = linked_section; + m_linked_offset = linked_offset; +} + +const Section * +Section::GetLinkedSection () const +{ + return m_linked_section; +} + +uint64_t +Section::GetLinkedOffset () const +{ + return m_linked_offset; +} + +#pragma mark SectionList + +SectionList::SectionList () : + m_sections() +{ +} + + +SectionList::~SectionList () +{ +} + +uint32_t +SectionList::AddSection (SectionSP& sect_sp) +{ + uint32_t section_index = m_sections.size(); + m_sections.push_back(sect_sp); + return section_index; +} + +uint32_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; +} + +uint32_t +SectionList::AddUniqueSection (SectionSP& sect_sp) +{ + uint32_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, 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::GetSize () const +{ + return m_sections.size(); +} + +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 (uint32_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) + { + 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)->GetName() == section_dstr) + { + sect_sp = *sect_iter; + } + else + { + sect_sp = (*sect_iter)->GetChildren().FindSectionByName(section_dstr); + } + } + } + return sect_sp; +} +// +//SectionSP +//SectionList::FindSectionByNames (const char *s, ...) const +//{ +// SectionSP sect_sp; +// va_list ap; +// va_start(ap, s); +// uint32_t idx = 0; +// for (const char *sect_name = s; sect_name != NULL; sect_name = va_arg(ap, const char *)) +// { +// printf("[%u] %s\n", idx++, sect_name); +// } +// va_end(ap); +// 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::GetSharedPointer (const Section *section, bool check_children) const +{ + SectionSP sect_sp; + if (section) + { + 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->get() == section) + { + sect_sp = *sect_iter; + break; + } + else if (check_children) + { + sect_sp = (*sect_iter)->GetChildren().GetSharedPointer (section, true); + } + } + } + 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; +} + + +SectionSP +SectionList::FindSectionContainingLinkedFileAddress (addr_t vm_addr) 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->ContainsLinkedFileAddress (vm_addr)) + { + sect_sp = *sect_iter; + break; + } + } + return sect_sp; +} + +bool +SectionList::ContainsSection(user_id_t sect_id) const +{ + return FindSectionByID (sect_id).get() != NULL; +} + +void +SectionList::Dump (Stream *s, Process *process, bool show_header) const +{ + if (show_header && !m_sections.empty()) + { + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->PutCString( "SectionList\n"); + s->IndentMore(); + s->Printf("%*s", 2*(sizeof(void *) + 2), ""); + s->Indent(); + s->Printf("SectID %s Address File Off. File Size Flags Section Name\n", process ? "Load" : "File"); + s->Printf("%*s", 2*(sizeof(void *) + 2), ""); + 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, process); + } + + 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/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp new file mode 100644 index 00000000000..2786fe40720 --- /dev/null +++ b/lldb/source/Core/SourceManager.cpp @@ -0,0 +1,305 @@ +//===-- 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/Core/SourceManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +static inline bool is_newline_char(char ch) +{ + return ch == '\n' || ch == '\r'; +} + + +//---------------------------------------------------------------------- +// SourceManager constructor +//---------------------------------------------------------------------- +SourceManager::SourceManager() : + m_file_cache (), + m_last_file_sp (), + m_last_file_line (0), + m_last_file_context_before (0), + m_last_file_context_after (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SourceManager::~SourceManager() +{ +} + +size_t +SourceManager::DisplaySourceLines +( + const FileSpec &file_spec, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + Stream *s +) +{ + m_last_file_sp = GetFile (file_spec); + m_last_file_line = line + context_after + 1; + m_last_file_context_before = context_before; + m_last_file_context_after = context_after; + if (m_last_file_sp.get()) + return m_last_file_sp->DisplaySourceLines (line, context_before, context_after, s); + + return 0; +} + +SourceManager::FileSP +SourceManager::GetFile (const FileSpec &file_spec) +{ + FileSP file_sp; + FileCache::iterator pos = m_file_cache.find(file_spec); + if (pos != m_file_cache.end()) + file_sp = pos->second; + else + { + file_sp.reset (new File (file_spec)); + m_file_cache[file_spec] = file_sp; + } + return file_sp; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile +( + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s +) +{ + if (line == 0) + { + if (m_last_file_line != 0 + && m_last_file_line != UINT32_MAX) + line = m_last_file_line + context_before; + else + line = 1; + } + + m_last_file_line = line + context_after + 1; + m_last_file_context_before = context_before; + m_last_file_context_after = context_after; + + if (context_before == UINT32_MAX) + context_before = 0; + if (context_after == UINT32_MAX) + context_after = 10; + + if (m_last_file_sp.get()) + { + const uint32_t start_line = line <= context_before ? 1 : line - context_before; + const uint32_t end_line = line + context_after; + uint32_t curr_line; + for (curr_line = start_line; curr_line <= end_line; ++curr_line) + { + if (!m_last_file_sp->LineIsValid (curr_line)) + { + m_last_file_line = UINT32_MAX; + break; + } + + s->Printf("%4u %2.2s\t", curr_line, curr_line == line ? current_line_cstr : ""); + if (m_last_file_sp->DisplaySourceLines (curr_line, 0, 0, s) == 0) + { + m_last_file_line = UINT32_MAX; + break; + } + } + } + return 0; +} + +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 +) +{ + bool same_as_previous = m_last_file_sp && m_last_file_sp->FileSpecMatches (file_spec); + + if (!same_as_previous) + m_last_file_sp = GetFile (file_spec); + + if (line == 0) + { + if (!same_as_previous) + m_last_file_line = 0; + } + + return DisplaySourceLinesWithLineNumbersUsingLastFile (line, context_before, context_after, current_line_cstr, s); +} + +size_t +SourceManager::DisplayMoreWithLineNumbers (Stream *s) +{ + if (m_last_file_sp) + { + if (m_last_file_line == UINT32_MAX) + return 0; + DisplaySourceLinesWithLineNumbersUsingLastFile (0, m_last_file_context_before, m_last_file_context_after, "", s); + } + return 0; +} + + + +SourceManager::File::File(const FileSpec &file_spec) : + m_file_spec(file_spec), + m_data_sp(file_spec.ReadFileContents ()), + m_offsets() +{ +} + +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) +{ + 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; +} + +bool +SourceManager::File::FileSpecMatches (const FileSpec &file_spec) +{ + return FileSpec::Compare (m_file_spec, file_spec, false) == 0; +} + + +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)) + { + 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"); + } + + } + else + { + // Calculate all line offsets up to "line" + assert(!"Not implemented yet"); + } + return false; +} diff --git a/lldb/source/Core/State.cpp b/lldb/source/Core/State.cpp new file mode 100644 index 00000000000..cf9a8345818 --- /dev/null +++ b/lldb/source/Core/State.cpp @@ -0,0 +1,87 @@ +//===-- 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" + +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 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; +} + +bool +lldb_private::StateIsRunningState (StateType state) +{ + switch (state) + { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + return true; + + case eStateDetached: + case eStateInvalid: + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + default: + break; + } + return false; +} + +bool +lldb_private::StateIsStoppedState (StateType state) +{ + switch (state) + { + case eStateInvalid: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + default: + break; + + case eStateUnloaded: + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + return true; + } + return false; +} diff --git a/lldb/source/Core/Stream.cpp b/lldb/source/Core/Stream.cpp new file mode 100644 index 00000000000..a0de2d431e9 --- /dev/null +++ b/lldb/source/Core/Stream.cpp @@ -0,0 +1,776 @@ +//===-- 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" + +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 (eByteOrderHost), + 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". +//------------------------------------------------------------------ +int +Stream::PutSLEB128 (int64_t sval) +{ + int bytes_written = 0; + if (m_flags.IsSet(eBinary)) + { + bool more = true; + bool negative = (sval < 0); + while (more) + { + uint8_t byte = sval & 0x7fu; + sval >>= 7; + assert((!negative && sval >= 0) || (negative && sval < 0)); + /* 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%lli", sval); + } + + return bytes_written; + +} + +//------------------------------------------------------------------ +// Put an ULEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +int +Stream::PutULEB128 (uint64_t uval) +{ + int bytes_written = 0; + if (m_flags.IsSet(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%llx", uval); + } + return bytes_written; +} + +//------------------------------------------------------------------ +// Print a raw NULL terminated C string to the stream using the +// printf format in "format". +//------------------------------------------------------------------ +int +Stream::PutCString (const char *cstr) +{ + int cstr_len = strlen(cstr); + // when in binary mode, emit the NULL terminator + if (m_flags.IsSet(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, int 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*llx%s", prefix, addr_width, addr, suffix); + Printf ("%s0x%0*llx%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, int addr_size, const char *prefix, const char *suffix) +{ + if (prefix != NULL) + PutCString (prefix); + Address (lo_addr, addr_size, "["); + Address (hi_addr, addr_size, "-", ")"); +} + + +int +Stream::PutChar (char ch) +{ + return Write (&ch, 1); +} + + +//------------------------------------------------------------------ +// Print some formatted output to the stream. +//------------------------------------------------------------------ +int +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. +//------------------------------------------------------------------ +int +Stream::PrintfVarArg (const char *format, va_list args) +{ + char str[1024]; + va_list args_copy; + + va_copy (args_copy, args); + + int bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + int length = vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + va_end (args); + // Include the NULL termination byte for binary output + if (m_flags.IsSet(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.IsSet(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 +//------------------------------------------------------------------ +int +Stream::EOL() +{ + return PutChar ('\n'); +} + +//------------------------------------------------------------------ +// Indent the current line using the current indentation level and +// print an optional string following the idenatation spaces. +//------------------------------------------------------------------ +int +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 ("%lli", 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 +//------------------------------------------------------------------ +uint8_t +Stream::GetAddressByteSize() const +{ + return m_addr_size; +} + +//------------------------------------------------------------------ +// Set the address size in bytes +//------------------------------------------------------------------ +void +Stream::SetAddressByteSize(uint8_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.IsSet(eVerbose); +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetDebug() const +{ + return m_flags.IsSet(eDebug); +} + +//------------------------------------------------------------------ +// The flags get accessor +//------------------------------------------------------------------ +Flags& +Stream::GetFlags() +{ + return m_flags; +} + +//------------------------------------------------------------------ +// The flags const get accessor +//------------------------------------------------------------------ +const Flags& +Stream::GetFlags() const +{ + return m_flags; +} + +int +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 + + int i; + char str[1024]; + int bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + int 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 (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 (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; +} + +int +Stream::PutNHex8 (size_t n, uint8_t uvalue) +{ + int bytes_written = 0; + for (int i=0; i<n; ++i) + bytes_written += _PutHex8 (uvalue, m_flags.IsSet(eAddPrefix)); + return bytes_written; +} + +int +Stream::_PutHex8 (uint8_t uvalue, bool add_prefix) +{ + int bytes_written = 0; + if (m_flags.IsSet(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; +} + +int +Stream::PutHex8 (uint8_t uvalue) +{ + return _PutHex8 (uvalue, m_flags.IsSet(eAddPrefix)); +} + +int +Stream::PutHex16 (uint16_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.IsSet(eAddPrefix); + int bytes_written = 0; + int byte; + if (byte_order == eByteOrderLittle) + { + for (byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + else + { + for (byte = sizeof(uvalue)-1; byte >= 0; --byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + return bytes_written; +} + +int +Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.IsSet(eAddPrefix); + int bytes_written = 0; + int byte; + if (byte_order == eByteOrderLittle) + { + for (byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + else + { + for (byte = sizeof(uvalue)-1; byte >= 0; --byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + return bytes_written; +} + +int +Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.IsSet(eAddPrefix); + int bytes_written = 0; + int byte; + if (byte_order == eByteOrderLittle) + { + for (byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + else + { + for (byte = sizeof(uvalue)-1; byte >= 0; --byte, add_prefix = false) + bytes_written += _PutHex8 (uvalue >> (byte * 8), add_prefix); + } + return bytes_written; +} + +int +Stream::PutMaxHex64 +( + uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order +) +{ + switch (byte_size) + { + case 1: return PutHex8 (uvalue); + case 2: return PutHex16 (uvalue); + case 4: return PutHex32 (uvalue); + case 8: return PutHex64 (uvalue); + } + return 0; +} + +int +Stream::PutPointer (void *ptr) +{ + return PutRawBytes (&ptr, sizeof(ptr), eByteOrderHost, eByteOrderHost); +} + +int +Stream::PutFloat(float f, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&f, sizeof(f), eByteOrderHost, byte_order); +} + +int +Stream::PutDouble(double d, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&d, sizeof(d), eByteOrderHost, byte_order); +} + +int +Stream::PutLongDouble(long double ld, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + return PutRawBytes (&ld, sizeof(ld), eByteOrderHost, byte_order); +} + +int +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; + + int bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + int i; + bool binary_is_clear = m_flags.IsClear (eBinary); + m_flags.Set (eBinary); + if (src_byte_order == dst_byte_order) + { + for (i=0;i<src_len; ++i) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (i=src_len-1;i>=0; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_clear) + m_flags.Clear (eBinary); + + return bytes_written; +} + +int +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; + + int bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + int i; + bool binary_is_set = m_flags.IsSet(eBinary); + m_flags.Clear(eBinary); + if (src_byte_order == dst_byte_order) + { + for (i=0;i<src_len; ++i) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (i=src_len-1;i>=0; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return bytes_written; +} + +int +Stream::PutCStringAsRawHex8 (const char *s) +{ + int bytes_written = 0; + bool binary_is_set = m_flags.IsSet(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, eByteOrderHost); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderBig); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderHost); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderBig); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderHost); + 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/lldb/source/Core/StreamFile.cpp b/lldb/source/Core/StreamFile.cpp new file mode 100644 index 00000000000..9c6c508b49e --- /dev/null +++ b/lldb/source/Core/StreamFile.cpp @@ -0,0 +1,132 @@ +//===-- 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 +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// StreamFile constructor +//---------------------------------------------------------------------- +StreamFile::StreamFile () : + Stream (), + m_file (NULL), + m_path_name (), + m_close_file (false) +{ +} + +StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order, FILE *f) : + Stream (flags, addr_size, byte_order), + m_file(f), + m_path_name (), + m_close_file(false) +{ +} + +StreamFile::StreamFile(FILE *f) : + Stream (), + m_file(f), + m_path_name (), + m_close_file(false) +{ +} + +StreamFile::StreamFile(uint32_t flags, uint32_t addr_size, ByteOrder byte_order, const char *path, const char *permissions) : + Stream (flags, addr_size, byte_order), + m_file (NULL), + m_path_name (path), + m_close_file(false) +{ + Open(path, permissions); +} + +StreamFile::StreamFile(const char *path, const char *permissions) : + Stream (), + m_file (NULL), + m_path_name (path), + m_close_file(false) +{ + Open(path, permissions); +} + + +StreamFile::~StreamFile() +{ + Close (); +} + +void +StreamFile::Close () +{ + if (m_close_file && m_file != NULL) + ::fclose (m_file); + m_file = NULL; + m_close_file = false; +} + +bool +StreamFile::Open (const char *path, const char *permissions) +{ + Close(); + if (path && path[0]) + { + if ((m_path_name.size() == 0) + || (m_path_name.compare(path) != 0)) + m_path_name = path; + m_file = ::fopen (path, permissions); + if (m_file != NULL) + m_close_file = true; + } + return m_file != NULL; +} + +void +StreamFile::Flush () +{ + if (m_file) + ::fflush (m_file); +} + +int +StreamFile::Write (const void *s, size_t length) +{ + if (m_file) + return ::fwrite (s, 1, length, m_file); + return 0; +} + +FILE * +StreamFile::GetFileHandle() +{ + return m_file; +} + +void +StreamFile::SetFileHandle (FILE *file, bool close_file) +{ + Close(); + m_file = file; + m_close_file = close_file; +} + +const char * +StreamFile::GetFilePathname () +{ + if (m_path_name.size() == 0) + return NULL; + else + return m_path_name.c_str(); +} diff --git a/lldb/source/Core/StreamString.cpp b/lldb/source/Core/StreamString.cpp new file mode 100644 index 00000000000..ccd9f972582 --- /dev/null +++ b/lldb/source/Core/StreamString.cpp @@ -0,0 +1,81 @@ +//===-- 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 <libkern/OSByteOrder.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... +} + +int +StreamString::Write (const void *s, size_t length) +{ + m_packet.append ((char *)s, length); + return length; +} + +void +StreamString::Clear() +{ + m_packet.clear(); +} + +void +StreamString::Dump(FILE *f) +{ + int size = GetSize(); + if (size > 0) + fprintf(f, "%*.*s", size, size, m_packet.c_str()); +} + +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; +} + diff --git a/lldb/source/Core/StringList.cpp b/lldb/source/Core/StringList.cpp new file mode 100644 index 00000000000..cb96fb0f7b9 --- /dev/null +++ b/lldb/source/Core/StringList.cpp @@ -0,0 +1,200 @@ +//===-- 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 <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 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) +{ + uint32_t len = strings.GetSize(); + + for (int i = 0; i < len; ++i) + m_strings.push_back (strings.GetStringAtIndex(i)); +} + +uint32_t +StringList::GetSize () +{ + return m_strings.size(); +} + +const char * +StringList::GetStringAtIndex (size_t idx) +{ + if (idx < m_strings.size()) + return m_strings[idx].c_str(); + return NULL; +} + +void +StringList::Clear () +{ + m_strings.clear(); +} + +void +StringList::LongestCommonPrefix (std::string &common_prefix) +{ + //arg_sstr_collection::iterator pos, end = m_args.end(); + int pos = 0; + int end = m_strings.size(); + + if (pos == end) + common_prefix.clear(); + else + common_prefix = m_strings[pos]; + + for (++pos; pos != end; ++pos) + { + int 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 (int 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; + + int idx = 0; + while (idx < m_strings.size()) + { + if (m_strings[idx].empty()) + DeleteStringAtIndex(idx); + else + idx++; + } +} diff --git a/lldb/source/Core/TTYState.cpp b/lldb/source/Core/TTYState.cpp new file mode 100644 index 00000000000..6ba415832d4 --- /dev/null +++ b/lldb/source/Core/TTYState.cpp @@ -0,0 +1,203 @@ +//===-- TTYState.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/TTYState.h" +#include <fcntl.h> +#include <unistd.h> +#include <sys/signal.h> + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +TTYState::TTYState() : + m_fd(-1), + m_tflags(-1), + m_ttystate_err(-1), + m_ttystate(), + m_process_group(-1) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TTYState::~TTYState() +{ +} + +//---------------------------------------------------------------------- +// Save the current state of the TTY for the file descriptor "fd" +// and if "save_process_group" is true, attempt to save the process +// group info for the TTY. +//---------------------------------------------------------------------- +bool +TTYState::Save (int fd, bool save_process_group) +{ + if (fd >= 0 && ::isatty (fd)) + { + m_fd = fd; + m_tflags = ::fcntl (fd, F_GETFL, 0); + m_ttystate_err = ::tcgetattr (fd, &m_ttystate); + if (save_process_group) + m_process_group = ::tcgetpgrp (0); + else + m_process_group = -1; + } + else + { + m_fd = -1; + m_tflags = -1; + m_ttystate_err = -1; + m_process_group = -1; + } + return m_ttystate_err == 0; +} + +//---------------------------------------------------------------------- +// Restore the state of the TTY using the cached values from a +// previous call to Save(). +//---------------------------------------------------------------------- +bool +TTYState::Restore () const +{ + int result = 0; + if (IsValid()) + { + if (TFlagsIsValid()) + result = fcntl (m_fd, F_SETFL, m_tflags); + + if (TTYStateIsValid()) + result = tcsetattr (m_fd, TCSANOW, &m_ttystate); + + if (ProcessGroupIsValid()) + { + // Save the original signal handler. + void (*saved_sigttou_callback) (int) = NULL; + saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN); + // Set the process group + result = tcsetpgrp (m_fd, m_process_group); + // Restore the original signal handler. + signal (SIGTTOU, saved_sigttou_callback); + } + return true; + } + return false; +} + + + + +//---------------------------------------------------------------------- +// Returns true if this object has valid saved TTY state settings +// that can be used to restore a previous state. +//---------------------------------------------------------------------- +bool +TTYState::IsValid() const +{ + return (m_fd >= 0) && (TFlagsIsValid() || TTYStateIsValid()); +} + +//---------------------------------------------------------------------- +// Returns true if m_tflags is valid +//---------------------------------------------------------------------- +bool +TTYState::TFlagsIsValid() const +{ + return m_tflags != -1; +} + +//---------------------------------------------------------------------- +// Returns true if m_ttystate is valid +//---------------------------------------------------------------------- +bool +TTYState::TTYStateIsValid() const +{ + return m_ttystate_err == 0; +} + +//---------------------------------------------------------------------- +// Returns true if m_process_group is valid +//---------------------------------------------------------------------- +bool +TTYState::ProcessGroupIsValid() const +{ + return m_process_group != -1; +} + +//------------------------------------------------------------------ +// Constructor +//------------------------------------------------------------------ +TTYStateSwitcher::TTYStateSwitcher () : + m_currentState(UINT32_MAX) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +TTYStateSwitcher::~TTYStateSwitcher () +{ +} + +//------------------------------------------------------------------ +// Returns the number of states that this switcher contains +//------------------------------------------------------------------ +uint32_t +TTYStateSwitcher::GetNumberOfStates() const +{ + return sizeof(m_ttystates)/sizeof(TTYState); +} + +//------------------------------------------------------------------ +// Restore the state at index "idx". +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TTYStateSwitcher::Restore (uint32_t idx) const +{ + const uint32_t num_states = GetNumberOfStates(); + if (idx >= num_states) + return false; + + // See if we already are in this state? + if (m_currentState < num_states && (idx == m_currentState) && m_ttystates[idx].IsValid()) + return true; + + // Set the state to match the index passed in and only update the + // current state if there are no errors. + if (m_ttystates[idx].Restore()) + { + m_currentState = idx; + return true; + } + + // We failed to set the state. The tty state was invalid or not + // initialized. + return false; +} + +//------------------------------------------------------------------ +// Save the state at index "idx" for file descriptor "fd" and +// save the process group if requested. +// +// Returns true if the restore was successful, false otherwise. +//------------------------------------------------------------------ +bool +TTYStateSwitcher::Save (uint32_t idx, int fd, bool save_process_group) +{ + const uint32_t num_states = GetNumberOfStates(); + if (idx < num_states) + return m_ttystates[idx].Save(fd, save_process_group); + return false; +} + + diff --git a/lldb/source/Core/Timer.cpp b/lldb/source/Core/Timer.cpp new file mode 100644 index 00000000000..e0d3bab354a --- /dev/null +++ b/lldb/source/Core/Timer.cpp @@ -0,0 +1,235 @@ +//===-- 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 "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 +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> CategoryMap; +static pthread_key_t g_key; + + +static Mutex & +GetCategoryMutex() +{ + static Mutex g_category_mutex(Mutex::eMutexTypeNormal); + return g_category_mutex; +} + +static CategoryMap & +GetCategoryMap() +{ + static CategoryMap 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::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) + { + // 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(); + bool notify = false; + if (m_total_start.IsValid()) + { + m_total_ticks += (stop_time - m_total_start); + m_total_start.Clear(); + notify = true; + } + 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; + ::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()); + CategoryMap &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 CategoryMap::const_iterator& lhs, const CategoryMap::const_iterator& rhs) +{ + return lhs->second > rhs->second; +} + + +void +Timer::ResetCategoryTimes () +{ + Mutex::Locker locker (GetCategoryMutex()); + CategoryMap &category_map = GetCategoryMap(); + category_map.clear(); +} + +void +Timer::DumpCategoryTimes (Stream *s) +{ + Mutex::Locker locker (GetCategoryMutex()); + CategoryMap &category_map = GetCategoryMap(); + std::vector<CategoryMap::const_iterator> sorted_iterators; + CategoryMap::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); + } +}
\ No newline at end of file diff --git a/lldb/source/Core/UUID.cpp b/lldb/source/Core/UUID.cpp new file mode 100644 index 00000000000..1ccfdccfab0 --- /dev/null +++ b/lldb/source/Core/UUID.cpp @@ -0,0 +1,218 @@ +//===-- 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 +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +UUID::UUID() +{ + ::bzero (m_uuid, sizeof(m_uuid)); +} + +UUID::UUID(const UUID& rhs) +{ + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); +} + +UUID::UUID (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + if (uuid_bytes && num_uuid_bytes >= 16) + ::memcpy (m_uuid, uuid_bytes, sizeof (m_uuid)); + else + ::bzero (m_uuid, sizeof(m_uuid)); +} + +UUID::UUID (const uuid_t *uuid) +{ + if (uuid) + ::memcpy (m_uuid, uuid, sizeof (m_uuid)); + else + ::bzero (m_uuid, sizeof(m_uuid)); +} + +const UUID& +UUID::operator=(const UUID& rhs) +{ + if (this != &rhs) + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); + return *this; +} + +UUID::~UUID() +{ +} + +void +UUID::Clear() +{ + ::bzero (m_uuid, sizeof(m_uuid)); +} + +const void * +UUID::GetBytes() const +{ + return m_uuid; +} + +char * +UUID::GetAsCString (char *dst, size_t dst_len) const +{ + const uint8_t *u = (const uint8_t *)GetBytes(); + snprintf(dst, dst_len, "%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]); + return dst; +} + +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]); +} + +void +UUID::SetBytes (const void *uuid_bytes) +{ + if (uuid_bytes) + ::memcpy (m_uuid, uuid_bytes, sizeof (m_uuid)); + else + ::bzero (m_uuid, sizeof(m_uuid)); +} + +size_t +UUID::GetByteSize() +{ + return sizeof(UUID::ValueType); +} + +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]; +} + +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::SetfromCString (const char *cstr) +{ + if (cstr == NULL) + return 0; + + uint32_t uuid_byte_idx = 0; + const char *p = cstr; + + // Skip leading whitespace characters + while (isspace(*p)) + ++p; + + // Try and decode a UUID + while (*p != '\0') + { + if (isxdigit(*p) && 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 + m_uuid[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 == 16) + break; + } + else if (*p == '-') + { + // Skip dashes + p++; + } + else + { + // UUID values can only consist of hex characters and '-' chars + return 0; + } + } + // If we successfully decoded a UUID, return the amount of characters that + // were consumed + if (uuid_byte_idx == 16) + return p - cstr; + + // Else return zero to indicate we were not able to parse a UUID value + return 0; +} + + + +bool +lldb_private::operator == (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) == 0; +} + +bool +lldb_private::operator != (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) != 0; +} + +bool +lldb_private::operator < (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) < 0; +} + +bool +lldb_private::operator <= (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) <= 0; +} + +bool +lldb_private::operator > (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) > 0; +} + +bool +lldb_private::operator >= (const UUID &lhs, const UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), UUID::GetByteSize()) >= 0; +} diff --git a/lldb/source/Core/UserID.cpp b/lldb/source/Core/UserID.cpp new file mode 100644 index 00000000000..b1dd5a618c1 --- /dev/null +++ b/lldb/source/Core/UserID.cpp @@ -0,0 +1,73 @@ +//===-- 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" + +using namespace lldb; +using namespace lldb_private; + +UserID::UserID (user_id_t uid) : + m_uid(uid) +{ +} + +UserID::~UserID () +{ +} + +void +UserID::Clear () +{ + m_uid = LLDB_INVALID_UID; +} + + +user_id_t +UserID::GetID () const +{ + return m_uid; +} + +void +UserID::SetID (user_id_t uid) +{ + m_uid = uid; +} + +UserID::IDMatches::IDMatches (user_id_t uid) : + m_uid(uid) +{ +} + +bool +UserID::IDMatches::operator() (const UserID& rhs) const +{ + return m_uid == rhs.GetID(); +} + +Stream& +lldb_private::operator << (Stream& strm, const UserID& uid) +{ + strm.Printf("{0x%8.8x}", uid.GetID()); + return strm; +} + +bool +lldb_private::operator== (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() == rhs.GetID(); +} + +bool +lldb_private::operator!= (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() != rhs.GetID(); +} + diff --git a/lldb/source/Core/VMRange.cpp b/lldb/source/Core/VMRange.cpp new file mode 100644 index 00000000000..2f5d8ecf9ab --- /dev/null +++ b/lldb/source/Core/VMRange.cpp @@ -0,0 +1,100 @@ +//===-- 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" + +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; +} + + +void +VMRange::Dump(Stream *s, lldb::addr_t offset) const +{ + s->AddressRange(offset + GetBaseAddress(), offset + GetEndAddress(), sizeof (addr_t)); +} + +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/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp new file mode 100644 index 00000000000..c4c17dd0428 --- /dev/null +++ b/lldb/source/Core/Value.cpp @@ -0,0 +1,803 @@ +//===-- 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/Stream.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" + +using namespace lldb; +using namespace lldb_private; + +Value::Value() : + m_value(), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(const Scalar& scalar) : + m_value(scalar), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(int v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(unsigned int v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(unsigned long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(long long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(unsigned long long v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(float v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(double v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(long double v) : + m_value(v), + m_value_type(eValueTypeScalar), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ +} + +Value::Value(const uint8_t *bytes, int len) : + m_value(), + m_value_type(eValueTypeHostAddress), + m_context(NULL), + m_context_type(eContextTypeInvalid) +{ + 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_value_type(v.m_value_type), + m_context(v.m_context), + m_context_type(v.m_context_type) +{ + 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::CreateProxy() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->CreateProxy (); + + Value *ret = new Value; + ret->SetContext(eContextTypeValue, this); + return ret; +} + +Value * +Value::GetProxyTarget() +{ + if (m_context_type == eContextTypeValue) + return (Value*)m_context; + else + return NULL; +} + +//#include "clang/Lex/LiteralSupport.h" +//#include "clang/AST/ASTContext.h" +//#include "clang/Frontend/CompilerInstance.h" +// +//Value::Value (const char *data, llvm::CompilerInstance *compiler) +//{ +// clang::NumericLiteralParser parser(data, data + strlen(data), clang::SourceLocation(), +// compiler->getPreprocessor()); +// if (parser.had_error) +// { +// } +// else if (parser.isBool) +// { +// APInt value; +// parser.GetIntegerValue(value); +// } +// else if (parser.isLong) +// { +// } +// else if (parser.isLongLong) +// { +// } +// else if (parser.isFloat) +// { +// } +// +//} +// +void +Value::Dump (Stream* strm) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->Dump (strm); + return; + } + + 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)); +} + +void * +Value::GetOpaqueClangQualType() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetOpaqueClangQualType (); + + if (m_context_type == eContextTypeOpaqueClangQualType) + return m_context; + + return NULL; +} + +Value::ValueType +Value::GetValueType() const +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueType (); + + return m_value_type; +} + +lldb::AddressType +Value::GetValueAddressType () const +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueAddressType (); + + switch (m_value_type) + { + default: + case eValueTypeScalar: + break; + case eValueTypeLoadAddress: return eAddressTypeLoad; + case eValueTypeFileAddress: return eAddressTypeFile; + case eValueTypeHostAddress: return eAddressTypeHost; + } + return eAddressTypeInvalid; +} + + +Value::ContextType +Value::GetContextType() const +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetContextType (); + + return m_context_type; +} + +void +Value::SetValueType (Value::ValueType value_type) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->SetValueType(value_type); + return; + } + + m_value_type = value_type; +} + +void +Value::ClearContext () +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->ClearContext(); + return; + } + + m_context = NULL; + m_context_type = eContextTypeInvalid; +} + +void +Value::SetContext (Value::ContextType context_type, void *p) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->SetContext(context_type, p); + return; + } + + m_context_type = context_type; + m_context = p; +} + +RegisterInfo * +Value::GetRegisterInfo() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetRegisterInfo(); + + if (m_context_type == eContextTypeDCRegisterInfo) + return static_cast<RegisterInfo *> (m_context); + return NULL; +} + +Type * +Value::GetType() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetType(); + + if (m_context_type == eContextTypeDCType) + return static_cast<Type *> (m_context); + return NULL; +} + +Scalar & +Value::GetScalar() +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetScalar(); + + return m_value; +} + +void +Value::ResizeData(int len) +{ + if (m_context_type == eContextTypeValue) + { + ((Value*)m_context)->ResizeData(len); + return; + } + + m_value_type = eValueTypeHostAddress; + m_data_buffer.SetByteSize(len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +bool +Value::ValueOf(ExecutionContext *exe_ctx, clang::ASTContext *ast_context) +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->ValueOf(exe_ctx, ast_context); + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + case eContextTypeOpaqueClangQualType: // clang::Type * + case eContextTypeDCRegisterInfo: // RegisterInfo * + case eContextTypeDCType: // Type * + break; + + case eContextTypeDCVariable: // Variable * + ResolveValue(exe_ctx, ast_context); + return true; + } + return false; +} + +size_t +Value::GetValueByteSize (clang::ASTContext *ast_context, Error *error_ptr) +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueByteSize(ast_context, error_ptr); + + size_t byte_size = 0; + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + // If we have no context, there is no way to know how much memory to read + if (error_ptr) + error_ptr->SetErrorString ("Invalid context type, there is no way to know how much memory to read."); + break; + + case eContextTypeOpaqueClangQualType: + if (ast_context == NULL) + { + if (error_ptr) + error_ptr->SetErrorString ("Can't determine size of opaque clang type with NULL ASTContext *."); + } + else + { + uint64_t bit_width = ClangASTContext::GetTypeBitSize (ast_context, m_context); + byte_size = (bit_width + 7 ) / 8; + } + break; + + case eContextTypeDCRegisterInfo: // RegisterInfo * + if (GetRegisterInfo()) + byte_size = GetRegisterInfo()->byte_size; + else if (error_ptr) + error_ptr->SetErrorString ("Can't determine byte size with NULL RegisterInfo *."); + + break; + + case eContextTypeDCType: // Type * + if (GetType()) + byte_size = GetType()->GetByteSize(); + else if (error_ptr) + error_ptr->SetErrorString ("Can't determine byte size with NULL Type *."); + break; + + case eContextTypeDCVariable: // Variable * + if (GetVariable()) + byte_size = GetVariable()->GetType()->GetByteSize(); + else if (error_ptr) + error_ptr->SetErrorString ("Can't determine byte size with NULL Variable *."); + 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; +} + +void * +Value::GetValueOpaqueClangQualType () +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueOpaqueClangQualType(); + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + break; + + case eContextTypeOpaqueClangQualType: + return m_context; + + case eContextTypeDCRegisterInfo: + break; // TODO: Eventually convert into a clang type? + + case eContextTypeDCType: + if (GetType()) + return GetType()->GetOpaqueClangQualType(); + break; + + case eContextTypeDCVariable: + if (GetVariable()) + return GetVariable()->GetType()->GetOpaqueClangQualType(); + break; + } + + return NULL; +} + +lldb::Format +Value::GetValueDefaultFormat () +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueDefaultFormat(); + + switch (m_context_type) + { + default: + case eContextTypeInvalid: + break; + + case eContextTypeOpaqueClangQualType: + return Type::GetFormat (m_context); + + case eContextTypeDCRegisterInfo: + if (GetRegisterInfo()) + return GetRegisterInfo()->format; + break; + + case eContextTypeDCType: + if (GetType()) + return GetType()->GetFormat(); + break; + + case eContextTypeDCVariable: + if (GetVariable()) + return GetVariable()->GetType()->GetFormat(); + break; + + } + + // Return a good default in case we can't figure anything out + return eFormatHex; +} + +Error +Value::GetValueAsData (ExecutionContext *exe_ctx, clang::ASTContext *ast_context, DataExtractor &data, uint32_t data_offset) +{ + if (m_context_type == eContextTypeValue) + return ((Value*)m_context)->GetValueAsData(exe_ctx, ast_context, data, data_offset); + + data.Clear(); + + Error error; + lldb::addr_t address = LLDB_INVALID_ADDRESS; + lldb::AddressType address_type = eAddressTypeFile; + switch (m_value_type) + { + default: + error.SetErrorStringWithFormat("Invalid value type %i.\n", m_value_type); + break; + + case eValueTypeScalar: + data.SetByteOrder (eByteOrderHost); + data.SetAddressByteSize(sizeof(void *)); + if (m_value.GetData (data)) + return error; // Success; + error.SetErrorStringWithFormat("Extracting data from value failed.\n"); + break; + + case eValueTypeLoadAddress: + if (exe_ctx == NULL) + { + error.SetErrorString ("Can't read memory (no execution context)."); + } + else if (exe_ctx->process == NULL) + { + error.SetErrorString ("Can't read memory (invalid process)."); + } + else + { + address = m_value.ULongLong(LLDB_INVALID_ADDRESS); + address_type = eAddressTypeLoad; + data.SetByteOrder(exe_ctx->process->GetByteOrder()); + data.SetAddressByteSize(exe_ctx->process->GetAddressByteSize()); + } + break; + + case eValueTypeFileAddress: + { + // 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 (GetVariable()) + { + lldb::addr_t file_addr = m_value.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()); + address = so_addr.GetLoadAddress (exe_ctx->process); + if (address != LLDB_INVALID_ADDRESS) + address_type = eAddressTypeLoad; + } + if (address_type == eAddressTypeFile) + error.SetErrorStringWithFormat ("%s is not loaded.\n", var_sc.module_sp->GetFileSpec().GetFilename().AsCString()); + } + else + { + error.SetErrorStringWithFormat ("Unable to resolve the module for file address 0x%llx for variable '%s'.\n", file_addr, variable->GetName().AsCString("")); + } + } + else + { + error.SetErrorString ("Invalid file 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); + data.SetByteOrder(eByteOrderHost); + data.SetAddressByteSize(sizeof(void *)); + address_type = eAddressTypeHost; + break; + } + + // Bail if we encountered any errors + if (error.Fail()) + return error; + + if (address == LLDB_INVALID_ADDRESS) + { + error.SetErrorStringWithFormat ("Invalid %s address.\n", address_type == eAddressTypeHost ? "host" : "load"); + return error; + } + + // If we got here, we need to read the value from memory + uint32_t byte_size = GetValueByteSize (ast_context, &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 = (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) + { + if (exe_ctx->process->ReadMemory(address, dst, byte_size, error) != byte_size) + { + if (error.Success()) + error.SetErrorStringWithFormat("read %u bytes of memory from 0x%llx failed", (uint64_t)address, byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("Unsupported lldb::AddressType value (%i).\n", address_type); + } + } + else + { + error.SetErrorStringWithFormat ("Out of memory.\n"); + } + + return error; +} + +Scalar & +Value::ResolveValue(ExecutionContext *exe_ctx, clang::ASTContext *ast_context) +{ + Scalar scalar; + if (m_context_type == eContextTypeValue) + { + // Resolve the proxy + + Value * v = (Value*)m_context; + + m_value = v->m_value; + m_value_type = v->m_value_type; + m_context = v->m_context; + m_context_type = v->m_context_type; + + 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(); + } + } + + if (m_context_type == eContextTypeOpaqueClangQualType) + { + void *opaque_clang_qual_type = GetOpaqueClangQualType(); + switch (m_value_type) + { + case eValueTypeScalar: // raw scalar value + break; + + default: + case eValueTypeFileAddress: + m_value.Clear(); + break; + + case eValueTypeLoadAddress: // load address value + case eValueTypeHostAddress: // host address value (for memory in the process that is using liblldb) + { + lldb::AddressType address_type = m_value_type == eValueTypeLoadAddress ? eAddressTypeLoad : eAddressTypeHost; + lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS); + DataExtractor data; + if (Type::ReadFromMemory (exe_ctx, ast_context, opaque_clang_qual_type, addr, address_type, data)) + { + if (Type::GetValueAsScalar (ast_context, opaque_clang_qual_type, 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 == eContextTypeValue) + return ((Value*)m_context)->GetVariable(); + + if (m_context_type == eContextTypeDCVariable) + return static_cast<Variable *> (m_context); + return NULL; +} + + + +const char * +Value::GetValueTypeAsCString (ValueType value_type) +{ + switch (value_type) + { + case eValueTypeScalar: return "scalar"; + 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 eContextTypeOpaqueClangQualType: return "clang::Type *"; + case eContextTypeDCRegisterInfo: return "RegisterInfo *"; + case eContextTypeDCType: return "Type *"; + case eContextTypeDCVariable: 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/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp new file mode 100644 index 00000000000..93511febe8a --- /dev/null +++ b/lldb/source/Core/ValueObject.cpp @@ -0,0 +1,678 @@ +//===-- 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/Core/ValueObject.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "clang/AST/Type.h" +#include "llvm/Support/raw_ostream.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +static lldb::user_id_t g_value_obj_uid = 0; + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject () : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_update_id (0), // Value object lists always start at 1, value objects start at zero + m_name (), + m_data (), + m_value (), + m_error (), + m_flags (), + m_value_str(), + m_location_str(), + m_summary_str(), + m_children(), + m_synthetic_children() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ValueObject::~ValueObject () +{ +} + +user_id_t +ValueObject::GetUpdateID() const +{ + return m_update_id; +} + +bool +ValueObject::UpdateValueIfNeeded (ExecutionContextScope *exe_scope) +{ + if (exe_scope) + { + Process *process = exe_scope->CalculateProcess(); + if (process) + { + const user_id_t stop_id = process->GetStopID(); + if (m_update_id != stop_id) + { + m_value_str.clear(); + m_location_str.clear(); + m_summary_str.clear(); + + UpdateValue (exe_scope); + if (m_error.Success()) + m_update_id = stop_id; + } + } + } + return m_error.Success(); +} + +const DataExtractor & +ValueObject::GetDataExtractor () const +{ + return m_data; +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + return m_data; +} + +const Error & +ValueObject::GetError() const +{ + return m_error; +} + +const ConstString & +ValueObject::GetName() const +{ + return m_name; +} + +const char * +ValueObject::GetLocationAsCString (ExecutionContextScope *exe_scope) +{ + if (UpdateValueIfNeeded(exe_scope)) + { + if (m_location_str.empty()) + { + StreamString sstr; + + switch (m_value.GetValueType()) + { + default: + break; + + case Value::eValueTypeScalar: + if (m_value.GetContextType() == Value::eContextTypeDCRegisterInfo) + { + RegisterInfo *reg_info = m_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; + break; + } + } + m_location_str = "scalar"; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t addr_nibble_size = m_data.GetAddressByteSize() * 2; + sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size, m_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::GetValueIsValid () +{ + return m_flags.IsSet(eValueIsValid); +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + if (b) + m_flags.Set(eValueIsValid); + else + m_flags.Clear(eValueIsValid); +} + +bool +ValueObject::GetValueDidChange () const +{ + return m_flags.IsSet(eValueChanged); +} + +void +ValueObject::SetValueDidChange (bool value_changed) +{ + m_flags.Set(eValueChanged); +} + +ValueObjectSP +ValueObject::GetChildAtIndex (uint32_t idx, bool can_create) +{ + ValueObjectSP child_sp; + if (idx < GetNumChildren()) + { + // Check if we have already made the child value object? + if (can_create && m_children[idx].get() == NULL) + { + // 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[idx] = CreateChildAtIndex (idx, false, 0); + } + + child_sp = m_children[idx]; + } + return child_sp; +} + +uint32_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return ClangASTContext::GetIndexOfChildWithName (GetClangAST(), + GetOpaqueClangQualType(), + name.AsCString(), + omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + // when getting a child by name, it could be burried 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 + std::vector<uint32_t> child_indexes; + clang::ASTContext *clang_ast = GetClangAST(); + void *clang_type = GetOpaqueClangQualType(); + bool omit_empty_base_classes = true; + const size_t num_child_indexes = ClangASTContext::GetIndexOfChildMemberWithName (clang_ast, + clang_type, + name.AsCString(), + omit_empty_base_classes, + child_indexes); + ValueObjectSP child_sp; + 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; +} + + +uint32_t +ValueObject::GetNumChildren () +{ + if (m_flags.IsClear(eNumChildrenHasBeenSet)) + { + SetNumChildren (CalculateNumChildren()); + } + return m_children.size(); +} +void +ValueObject::SetNumChildren (uint32_t num_children) +{ + m_flags.Set(eNumChildrenHasBeenSet); + m_children.resize(num_children); +} + +void +ValueObject::SetName (const char *name) +{ + m_name.SetCString(name); +} + +void +ValueObject::SetName (const ConstString &name) +{ + m_name = name; +} + +ValueObjectSP +ValueObject::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + bool omit_empty_base_classes = true; + + 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; + const bool transparent_pointers = synthetic_array_member == false; + clang::ASTContext *clang_ast = GetClangAST(); + void *clang_type = GetOpaqueClangQualType(); + void *child_clang_type; + child_clang_type = ClangASTContext::GetChildClangTypeAtIndex (clang_ast, + GetName().AsCString(), + clang_type, + idx, + transparent_pointers, + omit_empty_base_classes, + child_name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + 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_sp.reset (new ValueObjectChild (this, + clang_ast, + child_clang_type, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset)); + } + return valobj_sp; +} + +const char * +ValueObject::GetSummaryAsCString (ExecutionContextScope *exe_scope) +{ + if (UpdateValueIfNeeded (exe_scope)) + { + if (m_summary_str.empty()) + { + void *clang_type = GetOpaqueClangQualType(); + + // See if this is a pointer to a C string? + uint32_t fixed_length = 0; + if (clang_type && ClangASTContext::IsCStringType (clang_type, fixed_length)) + { + Process *process = exe_scope->CalculateProcess(); + if (process != NULL) + { + StreamString sstr; + lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS; + lldb::AddressType cstr_address_type = eAddressTypeInvalid; + switch (GetValue().GetValueType()) + { + case Value::eValueTypeScalar: + cstr_address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + cstr_address_type = eAddressTypeLoad; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t data_offset = 0; + cstr_address = m_data.GetPointer(&data_offset); + cstr_address_type = m_value.GetValueAddressType(); + if (cstr_address_type == eAddressTypeInvalid) + cstr_address_type = eAddressTypeLoad; + } + break; + } + + if (cstr_address != LLDB_INVALID_ADDRESS) + { + DataExtractor data; + size_t bytes_read = 0; + std::vector<char> data_buffer; + std::vector<char> cstr_buffer; + size_t cstr_length; + Error error; + if (fixed_length > 0) + { + data_buffer.resize(fixed_length); + // Resize the formatted buffer in case every character + // uses the "\xXX" format and one extra byte for a NULL + cstr_buffer.resize(data_buffer.size() * 4 + 1); + data.SetData (data_buffer.data(), data_buffer.size(), eByteOrderHost); + bytes_read = process->ReadMemory (cstr_address, data_buffer.data(), fixed_length, error); + if (bytes_read > 0) + { + sstr << '"'; + cstr_length = data.Dump (&sstr, + 0, // Start offset in "data" + eFormatChar, // Print as characters + 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 + sstr << '"'; + } + } + else + { + const size_t k_max_buf_size = 256; + data_buffer.resize (k_max_buf_size + 1); + // NULL terminate in case we don't get the entire C string + data_buffer.back() = '\0'; + // Make a formatted buffer that can contain take 4 + // bytes per character in case each byte uses the + // "\xXX" format and one extra byte for a NULL + cstr_buffer.resize (k_max_buf_size * 4 + 1); + + data.SetData (data_buffer.data(), data_buffer.size(), eByteOrderHost); + size_t total_cstr_len = 0; + while ((bytes_read = process->ReadMemory (cstr_address, data_buffer.data(), k_max_buf_size, error)) > 0) + { + size_t len = strlen(data_buffer.data()); + if (len == 0) + break; + if (len > bytes_read) + len = bytes_read; + if (sstr.GetSize() == 0) + sstr << '"'; + + cstr_length = data.Dump (&sstr, + 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 + + if (len < k_max_buf_size) + break; + cstr_address += total_cstr_len; + } + if (sstr.GetSize() > 0) + sstr << '"'; + } + + if (sstr.GetSize() > 0) + m_summary_str.assign (sstr.GetData(), sstr.GetSize()); + } + } + } + } + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + + +const char * +ValueObject::GetValueAsCString (ExecutionContextScope *exe_scope) +{ + // If our byte size is zero this is an aggregate type that has children + if (ClangASTContext::IsAggregateType (GetOpaqueClangQualType()) == false) + { + if (UpdateValueIfNeeded(exe_scope)) + { + if (m_value_str.empty()) + { + const Value::ContextType context_type = m_value.GetContextType(); + + switch (context_type) + { + case Value::eContextTypeOpaqueClangQualType: + case Value::eContextTypeDCType: + case Value::eContextTypeDCVariable: + { + void *clang_type = GetOpaqueClangQualType (); + if (clang_type) + { + StreamString sstr; + lldb::Format format = Type::GetFormat(clang_type); + if (Type::DumpTypeValue(&sstr, + GetClangAST(), // The clang AST + clang_type, // The clang type to display + format, // Format to display this type with + 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 + m_value_str.swap(sstr.GetString()); + else + m_value_str.clear(); + } + } + break; + + case Value::eContextTypeDCRegisterInfo: + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + StreamString reg_sstr; + m_data.Dump(®_sstr, 0, reg_info->format, reg_info->byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + m_value_str.swap(reg_sstr.GetString()); + } + } + break; + } + } + } + } + if (m_value_str.empty()) + return NULL; + return m_value_str.c_str(); +} + +bool +ValueObject::SetValueFromCString (ExecutionContextScope *exe_scope, const char *value_str) +{ + // Make sure our value is up to date first so that our location and location + // type is valid. + if (!UpdateValueIfNeeded(exe_scope)) + return false; + + uint32_t count = 0; + lldb::Encoding encoding = Type::GetEncoding (GetOpaqueClangQualType(), count); + + char *end = NULL; + size_t byte_size = GetByteSize(); + switch (encoding) + { + case eEncodingInvalid: + return false; + + case eEncodingUint: + if (byte_size > sizeof(unsigned long long)) + { + return false; + } + else + { + unsigned long long ull_val = strtoull(value_str, &end, 0); + if (end && *end != '\0') + return false; + m_value = ull_val; + // Limit the bytes in our m_data appropriately. + m_value.GetScalar().GetData (m_data, byte_size); + } + break; + + case eEncodingSint: + if (byte_size > sizeof(long long)) + { + return false; + } + else + { + long long sll_val = strtoll(value_str, &end, 0); + if (end && *end != '\0') + return false; + m_value = sll_val; + // Limit the bytes in our m_data appropriately. + m_value.GetScalar().GetData (m_data, byte_size); + } + break; + + case eEncodingIEEE754: + { + const size_t byte_size = GetByteSize(); + const off_t byte_offset = GetByteOffset(); + uint8_t *dst = (uint8_t *)m_data.PeekData(byte_offset, byte_size); + if (dst != NULL) + { + // We are decoding a float into host byte order below, so make + // sure m_data knows what it contains. + m_data.SetByteOrder(eByteOrderHost); + const size_t converted_byte_size = ClangASTContext::ConvertStringToFloatValue ( + GetClangAST(), + GetOpaqueClangQualType(), + value_str, + dst, + byte_size); + + if (converted_byte_size == byte_size) + { + } + } + } + break; + + case eEncodingVector: + return false; + + default: + return false; + } + + // If we have made it here the value is in m_data and we should write it + // out to the target + return Write (); +} + +bool +ValueObject::Write () +{ + // Clear the update ID so the next time we try and read the value + // we try and read it again. + m_update_id = 0; + + // TODO: when Value has a method to write a value back, call it from here. + return false; + +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObjectSP& valobj_sp) +{ + m_synthetic_children[key] = valobj_sp; +} + +ValueObjectSP +ValueObject::GetSyntheticChild (const ConstString &key) const +{ + ValueObjectSP synthetic_child_sp; + std::map<ConstString, ValueObjectSP>::const_iterator pos = m_synthetic_children.find (key); + if (pos != m_synthetic_children.end()) + synthetic_child_sp = pos->second; + return synthetic_child_sp; +} + +bool +ValueObject::IsPointerType () +{ + return ClangASTContext::IsPointerType (GetOpaqueClangQualType()); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return ClangASTContext::IsPointerOrReferenceType(GetOpaqueClangQualType()); +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%i]", 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) + { + // We haven't made a synthetic array member for INDEX yet, so + // lets make one and cache it for any future reference. + synthetic_child_sp = CreateChildAtIndex(0, true, index); + + // Cache the value if we got one back... + if (synthetic_child_sp) + AddSyntheticChild(index_const_str, synthetic_child_sp); + } + } + return synthetic_child_sp; +} diff --git a/lldb/source/Core/ValueObjectChild.cpp b/lldb/source/Core/ValueObjectChild.cpp new file mode 100644 index 00000000000..dc10bb09b4c --- /dev/null +++ b/lldb/source/Core/ValueObjectChild.cpp @@ -0,0 +1,207 @@ +//===-- 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/dwarf.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.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, + clang::ASTContext *clang_ast, + void *clang_type, + const ConstString &name, + uint32_t byte_size, + int32_t byte_offset, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset +) : + ValueObject (), + m_parent (parent), + m_clang_ast (clang_ast), + 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_name = name; +} + +ValueObjectChild::~ValueObjectChild() +{ +} + +void * +ValueObjectChild::GetOpaqueClangQualType() +{ + return m_clang_type; +} + +lldb::ValueType +ValueObjectChild::GetValueType() const +{ + return m_parent->GetValueType(); +} + +uint32_t +ValueObjectChild::CalculateNumChildren() +{ + return ClangASTContext::GetNumChildren (m_clang_type, true); +} + +clang::ASTContext * +ValueObjectChild::GetClangAST () +{ + return m_clang_ast; +} + +size_t +ValueObjectChild::GetByteSize() +{ + return m_byte_size; +} + +off_t +ValueObjectChild::GetByteOffset() +{ + return m_byte_offset; +} + +uint32_t +ValueObjectChild::GetBitfieldBitSize() +{ + return m_bitfield_bit_size; +} + +uint32_t +ValueObjectChild::GetBitfieldBitOffset() +{ + return m_bitfield_bit_offset; +} + +ConstString +ValueObjectChild::GetTypeName() +{ + if (m_type_name.IsEmpty()) + { + m_type_name = Type::GetClangTypeName (GetOpaqueClangQualType()); + if (m_type_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = m_type_name.AsCString(); + if (clang_type_name) + { + char bitfield_type_name[strlen(clang_type_name) + 32]; + ::snprintf (bitfield_type_name, sizeof(bitfield_type_name), "%s:%u", clang_type_name, m_bitfield_bit_size); + m_type_name.SetCString(bitfield_type_name); + } + } + } + } + return m_type_name; +} + +void +ValueObjectChild::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + SetValueIsValid (false); + ValueObject* parent = m_parent; + if (parent) + { + if (parent->UpdateValueIfNeeded(exe_scope)) + { + m_value.SetContext(Value::eContextTypeOpaqueClangQualType, m_clang_type); + + // 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 (ClangASTContext::IsPointerOrReferenceType (parent->GetOpaqueClangQualType())) + { + uint32_t offset = 0; + m_value.GetScalar() = parent->GetDataExtractor().GetPointer(&offset); + // For pointers, m_byte_offset should only ever be set if we + // ValueObject::GetSyntheticArrayMemberFromPointer() was called + if (ClangASTContext::IsPointerType (parent->GetOpaqueClangQualType()) && m_byte_offset) + m_value.GetScalar() += m_byte_offset; + if (value_type == Value::eValueTypeScalar || + value_type == Value::eValueTypeFileAddress) + m_value.SetValueType (Value::eValueTypeLoadAddress); + } + 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 || addr == 0) + { + m_error.SetErrorStringWithFormat("Parent address is invalid: 0x%llx.\n", addr); + break; + } + // Set this object's scalar value to the address of its + // value be 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 (exe_scope); + m_error = m_value.GetValueAsData (&exe_ctx, GetClangAST (), m_data, 0); + } + } + else + { + m_error.SetErrorStringWithFormat("Parent failed to evaluate: %s.\n", parent->GetError().AsCString()); + } + } + else + { + m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject."); + } +} + + +bool +ValueObjectChild::IsInScope (StackFrame *frame) +{ + return m_parent->IsInScope (frame); +} + diff --git a/lldb/source/Core/ValueObjectList.cpp b/lldb/source/Core/ValueObjectList.cpp new file mode 100644 index 00000000000..50ae1de0985 --- /dev/null +++ b/lldb/source/Core/ValueObjectList.cpp @@ -0,0 +1,119 @@ +//===-- 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); +} + +uint32_t +ValueObjectList::GetSize() const +{ + return m_value_objects.size(); +} + +lldb::ValueObjectSP +ValueObjectList::GetValueObjectAtIndex (uint32_t idx) +{ + lldb::ValueObjectSP valobj_sp; + if (idx < m_value_objects.size()) + valobj_sp = m_value_objects[idx]; + return 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) + { + if ((*pos)->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) + { + if ((*pos)->GetID() == uid) + { + valobj_sp = *pos; + break; + } + } + return valobj_sp; +} + + +ValueObjectSP +ValueObjectList::FindValueObjectByPointer (ValueObject *valobj) +{ + ValueObjectSP valobj_sp; + collection::iterator pos, end = m_value_objects.end(); + + for (pos = m_value_objects.begin(); pos != end; ++pos) + { + if ((*pos).get() == valobj) + { + valobj_sp = *pos; + break; + } + } + return valobj_sp; +} diff --git a/lldb/source/Core/ValueObjectRegister.cpp b/lldb/source/Core/ValueObjectRegister.cpp new file mode 100644 index 00000000000..aae818eadd6 --- /dev/null +++ b/lldb/source/Core/ValueObjectRegister.cpp @@ -0,0 +1,331 @@ +//===-- 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/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 (RegisterContext *reg_ctx) : + ValueObject (), + m_reg_ctx (reg_ctx) +{ + assert (reg_ctx); + m_name.SetCString("Registers"); + SetValueIsValid (true); +} + +ValueObjectRegisterContext::~ValueObjectRegisterContext() +{ +} + +void * +ValueObjectRegisterContext::GetOpaqueClangQualType () +{ + return NULL; +} + +ConstString +ValueObjectRegisterContext::GetTypeName() +{ + ConstString empty_type_name; + return empty_type_name; +} + +uint32_t +ValueObjectRegisterContext::CalculateNumChildren() +{ + return m_reg_ctx->GetRegisterSetCount(); +} + +clang::ASTContext * +ValueObjectRegisterContext::GetClangAST () +{ + return NULL; +} + +size_t +ValueObjectRegisterContext::GetByteSize() +{ + return 0; +} + +void +ValueObjectRegisterContext::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + StackFrame *frame = exe_scope->CalculateStackFrame(); + if (frame) + m_reg_ctx = frame->GetRegisterContext(); + else + m_reg_ctx = NULL; + + SetValueIsValid (m_reg_ctx != NULL); +} + +ValueObjectSP +ValueObjectRegisterContext::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + + const uint32_t num_children = GetNumChildren(); + if (idx < num_children) + valobj_sp.reset (new ValueObjectRegisterSet(m_reg_ctx, idx)); + return valobj_sp; +} + + +#pragma mark - +#pragma mark ValueObjectRegisterSet + +ValueObjectRegisterSet::ValueObjectRegisterSet (RegisterContext *reg_ctx, uint32_t reg_set_idx) : + ValueObject (), + m_reg_ctx (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() +{ +} + +void * +ValueObjectRegisterSet::GetOpaqueClangQualType () +{ + return NULL; +} + +ConstString +ValueObjectRegisterSet::GetTypeName() +{ + return ConstString(); +} + +uint32_t +ValueObjectRegisterSet::CalculateNumChildren() +{ + const RegisterSet *reg_set = m_reg_ctx->GetRegisterSet(m_reg_set_idx); + if (reg_set) + return reg_set->num_registers; + return 0; +} + +clang::ASTContext * +ValueObjectRegisterSet::GetClangAST () +{ + return NULL; +} + +size_t +ValueObjectRegisterSet::GetByteSize() +{ + return 0; +} + +void +ValueObjectRegisterSet::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + SetValueDidChange (false); + StackFrame *frame = exe_scope->CalculateStackFrame(); + if (frame == NULL) + m_reg_ctx = NULL; + else + { + m_reg_ctx = frame->GetRegisterContext (); + if (m_reg_ctx) + { + const RegisterSet *reg_set = m_reg_ctx->GetRegisterSet (m_reg_set_idx); + if (reg_set == NULL) + m_reg_ctx = NULL; + else if (m_reg_set != reg_set) + { + SetValueDidChange (true); + m_name.SetCString(reg_set->name); + } + } + } + if (m_reg_ctx) + { + SetValueIsValid (true); + } + else + { + SetValueIsValid (false); + m_children.clear(); + } +} + + +ValueObjectSP +ValueObjectRegisterSet::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + if (m_reg_ctx && m_reg_set) + { + const uint32_t num_children = GetNumChildren(); + if (idx < num_children) + valobj_sp.reset (new ValueObjectRegister(m_reg_ctx, m_reg_set->registers[idx])); + } + return valobj_sp; +} + + +#pragma mark - +#pragma mark ValueObjectRegister + +ValueObjectRegister::ValueObjectRegister (RegisterContext *reg_ctx, uint32_t reg_num) : + ValueObject (), + m_reg_ctx (reg_ctx), + m_reg_info (NULL), + m_reg_num (reg_num), + m_type_name (), + m_clang_type (NULL) +{ + assert (reg_ctx); + m_reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num); + if (m_reg_info) + { + if (m_reg_info->name) + m_name.SetCString(m_reg_info->name); + else if (m_reg_info->alt_name) + m_name.SetCString(m_reg_info->alt_name); + } +} + +ValueObjectRegister::~ValueObjectRegister() +{ +} + +void * +ValueObjectRegister::GetOpaqueClangQualType () +{ + if (m_clang_type == NULL && m_reg_info) + { + Process *process = m_reg_ctx->CalculateProcess (); + if (process) + { + Module *exe_module = process->GetTarget().GetExecutableModule ().get(); + if (exe_module) + { + TypeList *type_list = exe_module->GetTypeList(); + if (type_list) + m_clang_type = type_list->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 = Type::GetClangTypeName (GetOpaqueClangQualType()); + return m_type_name; +} + +uint32_t +ValueObjectRegister::CalculateNumChildren() +{ + return 0; +} + +clang::ASTContext * +ValueObjectRegister::GetClangAST () +{ + Process *process = m_reg_ctx->CalculateProcess (); + if (process) + { + Module *exe_module = process->GetTarget().GetExecutableModule ().get(); + if (exe_module) + { + TypeList *type_list = exe_module->GetTypeList(); + if (type_list) + return type_list->GetClangASTContext().getASTContext(); + } + } + return NULL; +} + +size_t +ValueObjectRegister::GetByteSize() +{ + return m_reg_info->byte_size; +} + +void +ValueObjectRegister::UpdateValue (ExecutionContextScope *exe_scope) +{ + m_error.Clear(); + StackFrame *frame = exe_scope->CalculateStackFrame(); + if (frame) + { + m_reg_ctx = frame->GetRegisterContext(); + if (m_reg_ctx) + { + const RegisterInfo *reg_info = m_reg_ctx->GetRegisterInfoAtIndex(m_reg_num); + if (m_reg_info != reg_info) + { + m_reg_info = reg_info; + if (m_reg_info) + { + if (m_reg_info->name) + m_name.SetCString(m_reg_info->name); + else if (m_reg_info->alt_name) + m_name.SetCString(m_reg_info->alt_name); + } + } + } + } + else + { + m_reg_ctx = NULL; + m_reg_info = NULL; + } + + + if (m_reg_ctx && m_reg_info) + { + if (m_reg_ctx->ReadRegisterBytes (m_reg_num, m_data)) + { + m_value.SetContext(Value::eContextTypeDCRegisterInfo, (void *)m_reg_info); + m_value.SetValueType(Value::eValueTypeHostAddress); + m_value.GetScalar() = (uintptr_t)m_data.GetDataStart(); + SetValueIsValid (true); + return; + } + } + SetValueIsValid (false); +} + + diff --git a/lldb/source/Core/ValueObjectVariable.cpp b/lldb/source/Core/ValueObjectVariable.cpp new file mode 100644 index 00000000000..0b94535387f --- /dev/null +++ b/lldb/source/Core/ValueObjectVariable.cpp @@ -0,0 +1,167 @@ +//===-- 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/ValueObjectList.h" +#include "lldb/Core/Value.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" + +#include "clang/AST/Type.h" + + +using namespace lldb_private; + +ValueObjectVariable::ValueObjectVariable (lldb::VariableSP &var_sp) : + ValueObject(), + 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() +{ +} + +void * +ValueObjectVariable::GetOpaqueClangQualType () +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetOpaqueClangQualType(); + return NULL; +} + +ConstString +ValueObjectVariable::GetTypeName() +{ + Type * var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetName(); + ConstString empty_type_name; + return empty_type_name; +} + +uint32_t +ValueObjectVariable::CalculateNumChildren() +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetNumChildren(true); + return 0; +} + +clang::ASTContext * +ValueObjectVariable::GetClangAST () +{ + return m_variable_sp->GetType()->GetClangAST(); +} + +size_t +ValueObjectVariable::GetByteSize() +{ + return m_variable_sp->GetType()->GetByteSize(); +} + +lldb::ValueType +ValueObjectVariable::GetValueType() const +{ + if (m_variable_sp) + return m_variable_sp->GetScope(); + return lldb::eValueTypeInvalid; +} + + + +void +ValueObjectVariable::UpdateValue (ExecutionContextScope *exe_scope) +{ + SetValueIsValid (false); + m_error.Clear(); + + Variable *variable = m_variable_sp.get(); + DWARFExpression &expr = variable->LocationExpression(); + Value old_value(m_value); + ExecutionContext exe_ctx (exe_scope); + if (expr.Evaluate (&exe_ctx, GetClangAST(), NULL, m_value, &m_error)) + { + m_value.SetContext(Value::eContextTypeDCVariable, variable); + + 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, GetClangAST(), m_data, 0); + 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 (ClangASTContext::IsAggregateType (GetOpaqueClangQualType())) + { + // 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::eContextTypeDCVariable, variable); + m_error = value.GetValueAsData(&exe_ctx, GetClangAST(), m_data, 0); + } + break; + } + + SetValueIsValid (m_error.Success()); + } +} + + + +bool +ValueObjectVariable::IsInScope (StackFrame *frame) +{ + return m_variable_sp->IsInScope (frame); +} + |