| //===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SymbolFileDWARF.h" |
| |
| #include <algorithm> |
| #include <set> |
| |
| #include "lldb/Core/RegularExpression.h" |
| #include "lldb/Core/Stream.h" |
| |
| #include "DWARFDebugInfo.h" |
| #include "DWARFCompileUnit.h" |
| #include "DWARFDebugAranges.h" |
| #include "DWARFDebugInfoEntry.h" |
| #include "DWARFFormValue.h" |
| |
| using namespace lldb_private; |
| using namespace std; |
| |
| //---------------------------------------------------------------------- |
| // Constructor |
| //---------------------------------------------------------------------- |
| DWARFDebugInfo::DWARFDebugInfo() : |
| m_dwarf2Data(NULL), |
| m_compile_units() |
| { |
| } |
| |
| //---------------------------------------------------------------------- |
| // SetDwarfData |
| //---------------------------------------------------------------------- |
| void |
| DWARFDebugInfo::SetDwarfData(SymbolFileDWARF* dwarf2Data) |
| { |
| m_dwarf2Data = dwarf2Data; |
| m_compile_units.clear(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // BuildDIEAddressRangeTable |
| //---------------------------------------------------------------------- |
| bool |
| DWARFDebugInfo::BuildFunctionAddressRangeTable(DWARFDebugAranges* debug_aranges) |
| { |
| const uint32_t num_compile_units = GetNumCompileUnits(); |
| uint32_t idx; |
| for (idx = 0; idx < num_compile_units; ++idx) |
| { |
| DWARFCompileUnit* cu = GetCompileUnitAtIndex (idx); |
| if (cu) |
| { |
| cu->DIE()->BuildFunctionAddressRangeTable(m_dwarf2Data, cu, debug_aranges); |
| } |
| } |
| return !debug_aranges->IsEmpty(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // LookupAddress |
| //---------------------------------------------------------------------- |
| bool |
| DWARFDebugInfo::LookupAddress |
| ( |
| const dw_addr_t address, |
| const dw_offset_t hint_die_offset, |
| DWARFCompileUnitSP& cu_sp, |
| DWARFDebugInfoEntry** function_die, |
| DWARFDebugInfoEntry** block_die |
| ) |
| { |
| |
| if (hint_die_offset != DW_INVALID_OFFSET) |
| cu_sp = GetCompileUnit(hint_die_offset); |
| else |
| { |
| // Get a non const version of the address ranges |
| DWARFDebugAranges* debug_aranges = ((SymbolFileDWARF*)m_dwarf2Data)->DebugAranges(); |
| |
| if (debug_aranges != NULL) |
| { |
| // If we have an empty address ranges section, lets build a sorted |
| // table ourselves by going through all of the debug information so we |
| // can do quick subsequent searches. |
| |
| if (debug_aranges->IsEmpty()) |
| { |
| const uint32_t num_compile_units = GetNumCompileUnits(); |
| uint32_t idx; |
| for (idx = 0; idx < num_compile_units; ++idx) |
| { |
| DWARFCompileUnit* cu = GetCompileUnitAtIndex(idx); |
| if (cu) |
| cu->DIE()->BuildAddressRangeTable(m_dwarf2Data, cu, debug_aranges); |
| } |
| } |
| cu_sp = GetCompileUnit(debug_aranges->FindAddress(address)); |
| } |
| } |
| |
| if (cu_sp.get()) |
| { |
| if (cu_sp->LookupAddress(address, function_die, block_die)) |
| return true; |
| cu_sp.reset(); |
| } |
| else |
| { |
| // The hint_die_offset may have been a pointer to the actual item that |
| // we are looking for |
| DWARFDebugInfoEntry* die_ptr = GetDIEPtr(hint_die_offset, &cu_sp); |
| if (die_ptr) |
| { |
| if (cu_sp.get()) |
| { |
| if (function_die || block_die) |
| return die_ptr->LookupAddress(address, m_dwarf2Data, cu_sp.get(), function_die, block_die); |
| |
| // We only wanted the compile unit that contained this address |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| void |
| DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() |
| { |
| if (m_compile_units.empty()) |
| { |
| if (m_dwarf2Data != NULL) |
| { |
| uint32_t offset = 0; |
| const DataExtractor &debug_info_data = m_dwarf2Data->get_debug_info_data(); |
| while (debug_info_data.ValidOffset(offset)) |
| { |
| DWARFCompileUnitSP cu_sp(new DWARFCompileUnit(m_dwarf2Data)); |
| // Out of memory? |
| if (cu_sp.get() == NULL) |
| break; |
| |
| if (cu_sp->Extract(debug_info_data, &offset) == false) |
| break; |
| |
| m_compile_units.push_back(cu_sp); |
| |
| offset = cu_sp->GetNextCompileUnitOffset(); |
| } |
| } |
| } |
| } |
| |
| uint32_t |
| DWARFDebugInfo::GetNumCompileUnits() |
| { |
| ParseCompileUnitHeadersIfNeeded(); |
| return m_compile_units.size(); |
| } |
| |
| DWARFCompileUnit* |
| DWARFDebugInfo::GetCompileUnitAtIndex(uint32_t idx) |
| { |
| DWARFCompileUnit* cu = NULL; |
| if (idx < GetNumCompileUnits()) |
| cu = m_compile_units[idx].get(); |
| return cu; |
| } |
| |
| static bool CompileUnitOffsetLessThan (const DWARFCompileUnitSP& a, const DWARFCompileUnitSP& b) |
| { |
| return a->GetOffset() < b->GetOffset(); |
| } |
| |
| |
| static int |
| CompareDWARFCompileUnitSPOffset (const void *key, const void *arrmem) |
| { |
| const dw_offset_t key_cu_offset = *(dw_offset_t*) key; |
| const dw_offset_t cu_offset = ((DWARFCompileUnitSP *)arrmem)->get()->GetOffset(); |
| if (key_cu_offset < cu_offset) |
| return -1; |
| if (key_cu_offset > cu_offset) |
| return 1; |
| return 0; |
| } |
| |
| DWARFCompileUnitSP |
| DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr) |
| { |
| DWARFCompileUnitSP cu_sp; |
| uint32_t cu_idx = DW_INVALID_INDEX; |
| if (cu_offset != DW_INVALID_OFFSET) |
| { |
| ParseCompileUnitHeadersIfNeeded(); |
| |
| DWARFCompileUnitSP* match = (DWARFCompileUnitSP*)bsearch(&cu_offset, &m_compile_units[0], m_compile_units.size(), sizeof(DWARFCompileUnitSP), CompareDWARFCompileUnitSPOffset); |
| if (match) |
| { |
| cu_sp = *match; |
| cu_idx = match - &m_compile_units[0]; |
| } |
| } |
| if (idx_ptr) |
| *idx_ptr = cu_idx; |
| return cu_sp; |
| } |
| |
| DWARFCompileUnitSP |
| DWARFDebugInfo::GetCompileUnitContainingDIE(dw_offset_t die_offset) |
| { |
| DWARFCompileUnitSP cu_sp; |
| if (die_offset != DW_INVALID_OFFSET) |
| { |
| ParseCompileUnitHeadersIfNeeded(); |
| |
| CompileUnitColl::const_iterator end_pos = m_compile_units.end(); |
| CompileUnitColl::const_iterator pos; |
| |
| for (pos = m_compile_units.begin(); pos != end_pos; ++pos) |
| { |
| dw_offset_t cu_start_offset = (*pos)->GetOffset(); |
| dw_offset_t cu_end_offset = (*pos)->GetNextCompileUnitOffset(); |
| if (cu_start_offset <= die_offset && die_offset < cu_end_offset) |
| { |
| cu_sp = *pos; |
| break; |
| } |
| } |
| } |
| return cu_sp; |
| } |
| |
| //---------------------------------------------------------------------- |
| // Compare function DWARFDebugAranges::Range structures |
| //---------------------------------------------------------------------- |
| static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) |
| { |
| return die1.GetOffset() < die2.GetOffset(); |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // GetDIE() |
| // |
| // Get the DIE (Debug Information Entry) with the specified offset. |
| //---------------------------------------------------------------------- |
| DWARFDebugInfoEntry* |
| DWARFDebugInfo::GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) |
| { |
| DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); |
| if (cu_sp_ptr) |
| *cu_sp_ptr = cu_sp; |
| if (cu_sp.get()) |
| return cu_sp->GetDIEPtr(die_offset); |
| return NULL; // Not found in any compile units |
| } |
| |
| const DWARFDebugInfoEntry* |
| DWARFDebugInfo::GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) |
| { |
| DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); |
| if (cu_sp_ptr) |
| *cu_sp_ptr = cu_sp; |
| if (cu_sp.get()) |
| return cu_sp->GetDIEPtrContainingOffset(die_offset); |
| |
| return NULL; // Not found in any compile units |
| |
| } |
| |
| //---------------------------------------------------------------------- |
| // DWARFDebugInfo_ParseCallback |
| // |
| // A callback function for the static DWARFDebugInfo::Parse() function |
| // that gets parses all compile units and DIE's into an internate |
| // representation for further modification. |
| //---------------------------------------------------------------------- |
| |
| static dw_offset_t |
| DWARFDebugInfo_ParseCallback |
| ( |
| SymbolFileDWARF* dwarf2Data, |
| DWARFCompileUnitSP& cu_sp, |
| DWARFDebugInfoEntry* die, |
| const dw_offset_t next_offset, |
| const uint32_t curr_depth, |
| void* userData |
| ) |
| { |
| DWARFDebugInfo* debug_info = (DWARFDebugInfo*)userData; |
| DWARFCompileUnit* cu = cu_sp.get(); |
| if (die) |
| { |
| cu->AddDIE(*die); |
| } |
| else if (cu) |
| { |
| debug_info->AddCompileUnit(cu_sp); |
| } |
| |
| // Just return the current offset to parse the next CU or DIE entry |
| return next_offset; |
| } |
| |
| //---------------------------------------------------------------------- |
| // AddCompileUnit |
| //---------------------------------------------------------------------- |
| void |
| DWARFDebugInfo::AddCompileUnit(DWARFCompileUnitSP& cu) |
| { |
| m_compile_units.push_back(cu); |
| } |
| |
| /* |
| void |
| DWARFDebugInfo::AddDIE(DWARFDebugInfoEntry& die) |
| { |
| m_die_array.push_back(die); |
| } |
| */ |
| |
| |
| |
| |
| //---------------------------------------------------------------------- |
| // Parse |
| // |
| // Parses the .debug_info section and uses the .debug_abbrev section |
| // and various other sections in the SymbolFileDWARF class and calls the |
| // supplied callback function each time a compile unit header, or debug |
| // information entry is successfully parsed. This function can be used |
| // for different tasks such as parsing the file contents into a |
| // structured data, dumping, verifying and much more. |
| //---------------------------------------------------------------------- |
| void |
| DWARFDebugInfo::Parse(SymbolFileDWARF* dwarf2Data, Callback callback, void* userData) |
| { |
| if (dwarf2Data) |
| { |
| uint32_t offset = 0; |
| uint32_t depth = 0; |
| DWARFCompileUnitSP cu(new DWARFCompileUnit(dwarf2Data)); |
| if (cu.get() == NULL) |
| return; |
| DWARFDebugInfoEntry die; |
| |
| while (cu->Extract(dwarf2Data->get_debug_info_data(), &offset)) |
| { |
| const dw_offset_t next_cu_offset = cu->GetNextCompileUnitOffset(); |
| |
| depth = 0; |
| // Call the callback funtion with no DIE pointer for the compile unit |
| // and get the offset that we are to continue to parse from |
| offset = callback(dwarf2Data, cu, NULL, offset, depth, userData); |
| |
| // Make sure we are within our compile unit |
| if (offset < next_cu_offset) |
| { |
| // We are in our compile unit, parse starting at the offset |
| // we were told to parse |
| bool done = false; |
| while (!done && die.Extract(dwarf2Data, cu.get(), &offset)) |
| { |
| // Call the callback funtion with DIE pointer that falls within the compile unit |
| offset = callback(dwarf2Data, cu, &die, offset, depth, userData); |
| |
| if (die.IsNULL()) |
| { |
| if (depth) |
| --depth; |
| else |
| done = true; // We are done with this compile unit! |
| } |
| else if (die.HasChildren()) |
| ++depth; |
| } |
| } |
| |
| // Make sure the offset returned is valid, and if not stop parsing. |
| // Returning DW_INVALID_OFFSET from this callback is a good way to end |
| // all parsing |
| if (!dwarf2Data->get_debug_info_data().ValidOffset(offset)) |
| break; |
| |
| // See if during the callback anyone retained a copy of the compile |
| // unit other than ourselves and if so, let whomever did own the object |
| // and create a new one for our own use! |
| if (!cu.unique()) |
| cu.reset(new DWARFCompileUnit(dwarf2Data)); |
| |
| |
| // Make sure we start on a propper |
| offset = next_cu_offset; |
| } |
| } |
| } |
| |
| /* |
| typedef struct AddressRangeTag |
| { |
| dw_addr_t lo_pc; |
| dw_addr_t hi_pc; |
| dw_offset_t die_offset; |
| } AddressRange; |
| */ |
| struct DIERange |
| { |
| DIERange() : |
| range(), |
| lo_die_offset(), |
| hi_die_offset() |
| { |
| } |
| |
| DWARFDebugAranges::Range range; |
| dw_offset_t lo_die_offset; |
| dw_offset_t hi_die_offset; |
| }; |
| |
| typedef struct DwarfStat |
| { |
| DwarfStat() : count(0), byte_size(0) {} |
| uint32_t count; |
| uint32_t byte_size; |
| } DwarfStat; |
| |
| typedef map<dw_attr_t, DwarfStat> DwarfAttrStatMap; |
| |
| typedef struct DIEStat |
| { |
| DIEStat() : count(0), byte_size(0), attr_stats() {} |
| uint32_t count; |
| uint32_t byte_size; |
| DwarfAttrStatMap attr_stats; |
| } DIEStat; |
| |
| typedef map<dw_tag_t, DIEStat> DIEStatMap; |
| struct VerifyInfo |
| { |
| VerifyInfo(Stream* the_strm) : |
| strm(the_strm), |
| die_ranges(), |
| addr_range_errors(0), |
| sibling_errors(0), |
| die_stats() |
| { |
| } |
| |
| Stream* strm; |
| vector<DIERange> die_ranges; |
| uint32_t addr_range_errors; |
| uint32_t sibling_errors; |
| DIEStatMap die_stats; |
| |
| DISALLOW_COPY_AND_ASSIGN(VerifyInfo); |
| |
| }; |
| |
| |
| //---------------------------------------------------------------------- |
| // VerifyCallback |
| // |
| // A callback function for the static DWARFDebugInfo::Parse() function |
| // that gets called each time a compile unit header or debug information |
| // entry is successfully parsed. |
| // |
| // This function will verify the DWARF information is well formed by |
| // making sure that any DW_TAG_compile_unit tags that have valid address |
| // ranges (DW_AT_low_pc and DW_AT_high_pc) have no gaps in the address |
| // ranges of it contained DW_TAG_subprogram tags. Also the sibling chain |
| // and relationships are verified to make sure nothing gets hosed up |
| // when dead stripping occurs. |
| //---------------------------------------------------------------------- |
| |
| static dw_offset_t |
| VerifyCallback |
| ( |
| SymbolFileDWARF* dwarf2Data, |
| DWARFCompileUnitSP& cu_sp, |
| DWARFDebugInfoEntry* die, |
| const dw_offset_t next_offset, |
| const uint32_t curr_depth, |
| void* userData |
| ) |
| { |
| VerifyInfo* verifyInfo = (VerifyInfo*)userData; |
| |
| const DWARFCompileUnit* cu = cu_sp.get(); |
| Stream *s = verifyInfo->strm; |
| bool verbose = s->GetVerbose(); |
| if (die) |
| { |
| // die->Dump(dwarf2Data, cu, f); |
| const DWARFAbbreviationDeclaration* abbrevDecl = die->GetAbbreviationDeclarationPtr(); |
| // We have a DIE entry |
| if (abbrevDecl) |
| { |
| const dw_offset_t die_offset = die->GetOffset(); |
| const dw_offset_t sibling = die->GetAttributeValueAsReference(dwarf2Data, cu, DW_AT_sibling, DW_INVALID_OFFSET); |
| |
| if (sibling != DW_INVALID_OFFSET) |
| { |
| if (sibling <= next_offset) |
| { |
| if (verifyInfo->sibling_errors++ == 0) |
| s->Printf("ERROR\n"); |
| s->Printf(" 0x%8.8x: sibling attribyte (0x%8.8x) in this die is not valid: it is less than this DIE or some of its contents.\n", die->GetOffset(), sibling); |
| } |
| else if (sibling > verifyInfo->die_ranges.back().hi_die_offset) |
| { |
| if (verifyInfo->sibling_errors++ == 0) |
| s->Printf("ERROR\n"); |
| s->Printf(" 0x%8.8x: sibling attribute (0x%8.8x) in this DIE is not valid: it is greater than the end of the parent scope.\n", die->GetOffset(), sibling); |
| } |
| } |
| |
| if ((die_offset < verifyInfo->die_ranges.back().lo_die_offset) || (die_offset >= verifyInfo->die_ranges.back().hi_die_offset)) |
| { |
| if (verifyInfo->sibling_errors++ == 0) |
| s->Printf("ERROR\n"); |
| s->Printf(" 0x%8.8x: DIE offset is not within the parent DIE range {0x%8.8x}: (0x%8.8x - 0x%8.8x)\n", |
| die->GetOffset(), |
| verifyInfo->die_ranges.back().range.offset, |
| verifyInfo->die_ranges.back().lo_die_offset, |
| verifyInfo->die_ranges.back().hi_die_offset); |
| |
| } |
| |
| dw_tag_t tag = abbrevDecl->Tag(); |
| |
| // Keep some stats on this DWARF file |
| verifyInfo->die_stats[tag].count++; |
| verifyInfo->die_stats[tag].byte_size += (next_offset - die->GetOffset()); |
| |
| if (verbose) |
| { |
| DIEStat& tag_stat = verifyInfo->die_stats[tag]; |
| |
| const DataExtractor& debug_info = dwarf2Data->get_debug_info_data(); |
| |
| dw_offset_t offset = die->GetOffset(); |
| // Skip the abbreviation code so we are at the data for the attributes |
| debug_info.Skip_LEB128(&offset); |
| |
| const uint32_t numAttributes = abbrevDecl->NumAttributes(); |
| dw_attr_t attr; |
| dw_form_t form; |
| for (uint32_t idx = 0; idx < numAttributes; ++idx) |
| { |
| dw_offset_t start_offset = offset; |
| abbrevDecl->GetAttrAndFormByIndexUnchecked(idx, attr, form); |
| DWARFFormValue::SkipValue(form, debug_info, &offset, cu); |
| |
| if (tag_stat.attr_stats.find(attr) == tag_stat.attr_stats.end()) |
| { |
| tag_stat.attr_stats[attr].count = 0; |
| tag_stat.attr_stats[attr].byte_size = 0; |
| } |
| |
| tag_stat.attr_stats[attr].count++; |
| tag_stat.attr_stats[attr].byte_size += offset - start_offset; |
| } |
| } |
| |
| DWARFDebugAranges::Range range; |
| range.offset = die->GetOffset(); |
| |
| switch (tag) |
| { |
| case DW_TAG_compile_unit: |
| // Check for previous subroutines that were within a previous |
| // |
| // VerifyAddressRangesForCU(verifyInfo); |
| // Remember which compile unit we are dealing with so we can verify |
| // the address ranges within it (if any) are contiguous. The DWARF |
| // spec states that if a compile unit TAG has high and low PC |
| // attributes, there must be no gaps in the address ranges of it's |
| // contained subtroutines. If there are gaps, the high and low PC |
| // must not be in the DW_TAG_compile_unit's attributes. Errors like |
| // this can crop up when optimized code is dead stripped and the debug |
| // information isn't properly fixed up for output. |
| range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); |
| if (range.lo_pc != DW_INVALID_ADDRESS) |
| { |
| range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); |
| if (s->GetVerbose()) |
| { |
| s->Printf("\n CU "); |
| range.Dump(s); |
| } |
| } |
| else |
| { |
| range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_entry_pc, DW_INVALID_ADDRESS); |
| } |
| break; |
| |
| case DW_TAG_subprogram: |
| // If the DW_TAG_compile_unit that contained this function had a |
| // valid address range, add all of the valid subroutine address |
| // ranges to a collection of addresses which will be sorted |
| // and verified right before the next DW_TAG_compile_unit is |
| // processed to make sure that there are no gaps in the address |
| // range. |
| range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); |
| if (range.lo_pc != DW_INVALID_ADDRESS) |
| { |
| range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); |
| if (range.hi_pc != DW_INVALID_ADDRESS) |
| { |
| range.offset = die->GetOffset(); |
| bool valid = range.ValidRange(); |
| if (!valid || s->GetVerbose()) |
| { |
| s->Printf("\n FUNC "); |
| range.Dump(s); |
| if (!valid) |
| { |
| ++verifyInfo->addr_range_errors; |
| s->Printf(" ERROR: Invalid address range for function."); |
| } |
| } |
| |
| // Only add to our subroutine ranges if our compile unit has a valid address range |
| // if (valid && verifyInfo->die_ranges.size() >= 2 && verifyInfo->die_ranges[1].range.ValidRange()) |
| // verifyInfo->subroutine_ranges.InsertRange(range); |
| } |
| } |
| break; |
| |
| case DW_TAG_lexical_block: |
| case DW_TAG_inlined_subroutine: |
| { |
| range.lo_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, DW_INVALID_ADDRESS); |
| if (range.lo_pc != DW_INVALID_ADDRESS) |
| { |
| range.hi_pc = die->GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_high_pc, DW_INVALID_ADDRESS); |
| if (range.hi_pc != DW_INVALID_ADDRESS) |
| { |
| range.offset = die->GetOffset(); |
| bool valid = range.ValidRange(); |
| if (!valid || s->GetVerbose()) |
| { |
| s->Printf("\n BLCK "); |
| range.Dump(s); |
| if (!valid) |
| { |
| ++verifyInfo->addr_range_errors; |
| s->Printf(" ERROR: Invalid address range for block or inlined subroutine."); |
| } |
| } |
| } |
| } |
| } |
| break; |
| } |
| |
| if (range.ValidRange() && verifyInfo->die_ranges.back().range.ValidRange()) |
| { |
| if (!verifyInfo->die_ranges.back().range.Contains(range)) |
| { |
| ++verifyInfo->addr_range_errors; |
| s->Printf("\n "); |
| range.Dump(s); |
| s->Printf(" ERROR: Range is not in parent"); |
| verifyInfo->die_ranges.back().range.Dump(s); |
| } |
| } |
| |
| if (die->HasChildren()) |
| { |
| // Keep tabs on the valid address ranges for the current item to make |
| // sure that it all fits (make sure the sibling offsets got fixed up |
| // correctly if any functions were dead stripped). |
| DIERange die_range; |
| die_range.range = range; |
| die_range.lo_die_offset = next_offset; |
| die_range.hi_die_offset = sibling; |
| if (die_range.hi_die_offset == DW_INVALID_OFFSET) |
| die_range.hi_die_offset = verifyInfo->die_ranges.back().hi_die_offset; |
| verifyInfo->die_ranges.push_back(die_range); |
| } |
| } |
| else |
| { |
| // NULL entry |
| verifyInfo->die_ranges.pop_back(); |
| } |
| } |
| else |
| { |
| // cu->Dump(ostrm_ptr); // Dump the compile unit for the DIE |
| // We have a new comile unit header |
| verifyInfo->die_ranges.clear(); |
| DIERange die_range; |
| die_range.range.offset = cu->GetOffset(); |
| die_range.lo_die_offset = next_offset; |
| die_range.hi_die_offset = cu->GetNextCompileUnitOffset(); |
| verifyInfo->die_ranges.push_back(die_range); |
| } |
| |
| // Just return the current offset to parse the next CU or DIE entry |
| return next_offset; |
| } |
| |
| |
| class CompareDIEStatSizes |
| { |
| public: |
| bool operator() (const DIEStatMap::const_iterator& pos1, const DIEStatMap::const_iterator& pos2) const |
| { |
| return pos1->second.byte_size <= pos2->second.byte_size; |
| } |
| }; |
| |
| class CompareAttrDIEStatSizes |
| { |
| public: |
| bool operator() (const DwarfAttrStatMap::const_iterator& pos1, const DwarfAttrStatMap::const_iterator& pos2) const |
| { |
| return pos1->second.byte_size <= pos2->second.byte_size; |
| } |
| }; |
| |
| //---------------------------------------------------------------------- |
| // Verify |
| // |
| // Verifies the DWARF information is valid. |
| //---------------------------------------------------------------------- |
| void |
| DWARFDebugInfo::Verify(Stream *s, SymbolFileDWARF* dwarf2Data) |
| { |
| s->Printf("Verifying Compile Unit Header chain....."); |
| VerifyInfo verifyInfo(s); |
| verifyInfo.addr_range_errors = 0; |
| verifyInfo.sibling_errors = 0; |
| |
| bool verbose = s->GetVerbose(); |
| |
| uint32_t offset = 0; |
| if (verbose) |
| s->EOL(); |
| // vector<dw_offset_t> valid_cu_offsets; |
| DWARFCompileUnit cu (dwarf2Data); |
| bool success = true; |
| while ( success && dwarf2Data->get_debug_info_data().ValidOffset(offset+cu.Size()) ) |
| { |
| success = cu.Extract (dwarf2Data->get_debug_info_data(), &offset); |
| if (!success) |
| s->Printf("ERROR\n"); |
| // else |
| // valid_cu_offsets.push_back(cu.GetOffset()); |
| |
| cu.Verify(verifyInfo.strm); |
| offset = cu.GetNextCompileUnitOffset(); |
| } |
| |
| if (success) |
| s->Printf("OK\n"); |
| |
| s->Printf("Verifying address ranges and siblings..."); |
| if (verbose) |
| s->EOL(); |
| DWARFDebugInfo::Parse(dwarf2Data, VerifyCallback, &verifyInfo); |
| |
| // VerifyAddressRangesForCU(&verifyInfo); |
| |
| if (verifyInfo.addr_range_errors > 0) |
| s->Printf("\nERRORS - %u error(s) were found.\n", verifyInfo.addr_range_errors); |
| else |
| s->Printf("OK\n"); |
| |
| uint32_t total_category_sizes[kNumTagCategories] = {0}; |
| uint32_t total_category_count[kNumTagCategories] = {0}; |
| uint32_t total_die_count = 0; |
| uint32_t total_die_size = 0; |
| |
| typedef set<DIEStatMap::const_iterator, CompareDIEStatSizes> DIEStatBySizeMap; |
| |
| s->PutCString( "\n" |
| "DWARF Statistics\n" |
| "Count Size Size % Tag\n" |
| "-------- -------- -------- -------------------------------------------\n"); |
| DIEStatBySizeMap statBySizeMap; |
| DIEStatMap::const_iterator pos; |
| DIEStatMap::const_iterator end_pos = verifyInfo.die_stats.end(); |
| for (pos = verifyInfo.die_stats.begin(); pos != end_pos; ++pos) |
| { |
| const uint32_t die_count = pos->second.count; |
| const uint32_t die_size = pos->second.byte_size; |
| |
| statBySizeMap.insert(pos); |
| total_die_count += die_count; |
| total_die_size += die_size; |
| DW_TAG_CategoryEnum category = get_tag_category(pos->first); |
| total_category_sizes[category] += die_size; |
| total_category_count[category] += die_count; |
| } |
| |
| float total_die_size_float = total_die_size; |
| |
| DIEStatBySizeMap::const_reverse_iterator size_pos; |
| DIEStatBySizeMap::const_reverse_iterator size_pos_end = statBySizeMap.rend(); |
| float percentage; |
| for (size_pos = statBySizeMap.rbegin(); size_pos != size_pos_end; ++size_pos) |
| { |
| pos = *size_pos; |
| |
| const DIEStat& tag_stat = pos->second; |
| |
| const uint32_t die_count = tag_stat.count; |
| const uint32_t die_size = tag_stat.byte_size; |
| percentage = ((float)die_size/total_die_size_float)*100.0; |
| s->Printf("%7u %8u %2.2f%% %s\n", die_count, die_size, percentage, DW_TAG_value_to_name(pos->first)); |
| |
| const DwarfAttrStatMap& attr_stats = tag_stat.attr_stats; |
| if (!attr_stats.empty()) |
| { |
| typedef set<DwarfAttrStatMap::const_iterator, CompareAttrDIEStatSizes> DwarfAttrStatBySizeMap; |
| DwarfAttrStatBySizeMap attrStatBySizeMap; |
| DwarfAttrStatMap::const_iterator attr_stat_pos; |
| DwarfAttrStatMap::const_iterator attr_stat_pos_end = attr_stats.end(); |
| for (attr_stat_pos = attr_stats.begin(); attr_stat_pos != attr_stat_pos_end; ++attr_stat_pos) |
| { |
| attrStatBySizeMap.insert(attr_stat_pos); |
| } |
| |
| DwarfAttrStatBySizeMap::const_reverse_iterator attr_size_pos; |
| DwarfAttrStatBySizeMap::const_reverse_iterator attr_size_pos_end = attrStatBySizeMap.rend(); |
| for (attr_size_pos = attrStatBySizeMap.rbegin(); attr_size_pos != attr_size_pos_end; ++attr_size_pos) |
| { |
| attr_stat_pos = *attr_size_pos; |
| percentage = ((float)attr_stat_pos->second.byte_size/die_size)*100.0; |
| s->Printf("%7u %8u %2.2f%% %s\n", attr_stat_pos->second.count, attr_stat_pos->second.byte_size, percentage, DW_AT_value_to_name(attr_stat_pos->first)); |
| } |
| s->EOL(); |
| } |
| } |
| |
| s->Printf("-------- -------- -------- -------------------------------------------\n"); |
| s->Printf("%7u %8u 100.00% Total for all DIEs\n", total_die_count, total_die_size); |
| |
| float total_category_percentages[kNumTagCategories] = |
| { |
| ((float)total_category_sizes[TagCategoryVariable]/total_die_size_float)*100.0, |
| ((float)total_category_sizes[TagCategoryType]/total_die_size_float)*100.0, |
| ((float)total_category_sizes[TagCategoryProgram]/total_die_size_float)*100.0 |
| }; |
| |
| s->EOL(); |
| s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryVariable], total_category_sizes[TagCategoryVariable], total_category_percentages[TagCategoryVariable], "Total for variable related DIEs"); |
| s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryType], total_category_sizes[TagCategoryType], total_category_percentages[TagCategoryType], "Total for type related DIEs"); |
| s->Printf("%7u %8u %2.2f%% %s\n", total_category_count[TagCategoryProgram], total_category_sizes[TagCategoryProgram], total_category_percentages[TagCategoryProgram], "Total for program related DIEs"); |
| s->Printf("\n\n"); |
| } |
| |
| typedef struct DumpInfo |
| { |
| DumpInfo(Stream* init_strm, uint32_t off, uint32_t depth) : |
| strm(init_strm), |
| die_offset(off), |
| recurse_depth(depth), |
| found_depth(UINT_MAX), |
| found_die(false), |
| ancestors() |
| { |
| } |
| Stream* strm; |
| const uint32_t die_offset; |
| const uint32_t recurse_depth; |
| uint32_t found_depth; |
| bool found_die; |
| std::vector<DWARFDebugInfoEntry> ancestors; |
| |
| DISALLOW_COPY_AND_ASSIGN(DumpInfo); |
| } DumpInfo; |
| |
| //---------------------------------------------------------------------- |
| // DumpCallback |
| // |
| // A callback function for the static DWARFDebugInfo::Parse() function |
| // that gets called each time a compile unit header or debug information |
| // entry is successfully parsed. |
| // |
| // This function dump DWARF information and obey recurse depth and |
| // wether a single DIE is to be dumped (or all of the data). |
| //---------------------------------------------------------------------- |
| static dw_offset_t DumpCallback |
| ( |
| SymbolFileDWARF* dwarf2Data, |
| DWARFCompileUnitSP& cu_sp, |
| DWARFDebugInfoEntry* die, |
| const dw_offset_t next_offset, |
| const uint32_t curr_depth, |
| void* userData |
| ) |
| { |
| DumpInfo* dumpInfo = (DumpInfo*)userData; |
| |
| const DWARFCompileUnit* cu = cu_sp.get(); |
| |
| Stream *s = dumpInfo->strm; |
| bool show_parents = s->GetFlags().IsSet(DWARFDebugInfo::eDumpFlag_ShowAncestors); |
| |
| if (die) |
| { |
| // Are we dumping everything? |
| if (dumpInfo->die_offset == DW_INVALID_OFFSET) |
| { |
| // Yes we are dumping everything. Obey our recurse level though |
| if (curr_depth < dumpInfo->recurse_depth) |
| die->Dump(dwarf2Data, cu, s, 0); |
| } |
| else |
| { |
| // We are dumping a specific DIE entry by offset |
| if (dumpInfo->die_offset == die->GetOffset()) |
| { |
| // We found the DIE we were looking for, dump it! |
| if (show_parents) |
| { |
| s->SetIndentLevel(0); |
| const uint32_t num_ancestors = dumpInfo->ancestors.size(); |
| if (num_ancestors > 0) |
| { |
| for (uint32_t i=0; i<num_ancestors-1; ++i) |
| { |
| dumpInfo->ancestors[i].Dump(dwarf2Data, cu, s, 0); |
| s->IndentMore(); |
| } |
| } |
| } |
| |
| dumpInfo->found_depth = curr_depth; |
| |
| die->Dump(dwarf2Data, cu, s, 0); |
| |
| // Note that we found the DIE we were looking for |
| dumpInfo->found_die = true; |
| |
| // Since we are dumping a single DIE, if there are no children we are done! |
| if (!die->HasChildren() || dumpInfo->recurse_depth == 0) |
| return DW_INVALID_OFFSET; // Return an invalid address to end parsing |
| } |
| else if (dumpInfo->found_die) |
| { |
| // Are we done with all the children? |
| if (curr_depth <= dumpInfo->found_depth) |
| return DW_INVALID_OFFSET; |
| |
| // We have already found our DIE and are printing it's children. Obey |
| // our recurse depth and return an invalid offset if we get done |
| // dumping all the the children |
| if (dumpInfo->recurse_depth == UINT_MAX || curr_depth <= dumpInfo->found_depth + dumpInfo->recurse_depth) |
| die->Dump(dwarf2Data, cu, s, 0); |
| } |
| else if (dumpInfo->die_offset > die->GetOffset()) |
| { |
| if (show_parents) |
| dumpInfo->ancestors.back() = *die; |
| } |
| } |
| |
| // Keep up with our indent level |
| if (die->IsNULL()) |
| { |
| if (show_parents) |
| dumpInfo->ancestors.pop_back(); |
| |
| if (curr_depth <= 1) |
| return cu->GetNextCompileUnitOffset(); |
| else |
| s->IndentLess(); |
| } |
| else if (die->HasChildren()) |
| { |
| if (show_parents) |
| { |
| DWARFDebugInfoEntry null_die; |
| dumpInfo->ancestors.push_back(null_die); |
| } |
| s->IndentMore(); |
| } |
| } |
| else |
| { |
| if (cu == NULL) |
| s->PutCString("NULL - cu"); |
| // We have a compile unit, reset our indent level to zero just in case |
| s->SetIndentLevel(0); |
| |
| // See if we are dumping everything? |
| if (dumpInfo->die_offset == DW_INVALID_OFFSET) |
| { |
| // We are dumping everything |
| cu->Dump(s); |
| return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit |
| } |
| else |
| { |
| if (show_parents) |
| { |
| dumpInfo->ancestors.clear(); |
| dumpInfo->ancestors.resize(1); |
| } |
| |
| // We are dumping only a single DIE possibly with it's children and |
| // we must find it's compile unit before we can dump it properly |
| if (dumpInfo->die_offset < cu->GetFirstDIEOffset()) |
| { |
| // Not found, maybe the DIE offset provided wasn't correct? |
| // *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " was not found." << endl; |
| return DW_INVALID_OFFSET; |
| } |
| else |
| { |
| // See if the DIE is in this compile unit? |
| if (dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) |
| { |
| // This DIE is in this compile unit! |
| if (s->GetVerbose()) |
| cu->Dump(s); // Dump the compile unit for the DIE in verbose mode |
| |
| return next_offset; |
| // // We found our compile unit that contains our DIE, just skip to dumping the requested DIE... |
| // return dumpInfo->die_offset; |
| } |
| else |
| { |
| // Skip to the next compile unit as the DIE isn't in the current one! |
| return cu->GetNextCompileUnitOffset(); |
| } |
| } |
| } |
| } |
| |
| // Just return the current offset to parse the next CU or DIE entry |
| return next_offset; |
| } |
| |
| //---------------------------------------------------------------------- |
| // Dump |
| // |
| // Dump the information in the .debug_info section to the specified |
| // ostream. If die_offset is valid, a single DIE will be dumped. If the |
| // die_offset is invalid, all the DWARF information will be dumped. Both |
| // cases will obey a "recurse_depth" or how deep to traverse into the |
| // children of each DIE entry. A recurse_depth of zero will dump all |
| // compile unit headers. A recurse_depth of 1 will dump all compile unit |
| // headers and the DW_TAG_compile unit tags. A depth of 2 will also |
| // dump all types and functions. |
| //---------------------------------------------------------------------- |
| void |
| DWARFDebugInfo::Dump |
| ( |
| Stream *s, |
| SymbolFileDWARF* dwarf2Data, |
| const uint32_t die_offset, |
| const uint32_t recurse_depth |
| ) |
| { |
| DumpInfo dumpInfo(s, die_offset, recurse_depth); |
| s->PutCString(".debug_info contents"); |
| if (dwarf2Data->get_debug_info_data().GetByteSize() > 0) |
| { |
| if (die_offset == DW_INVALID_OFFSET) |
| s->PutCString(":\n"); |
| else |
| { |
| s->Printf(" for DIE entry at .debug_info[0x%8.8x]", die_offset); |
| if (recurse_depth != UINT_MAX) |
| s->Printf(" recursing %u levels deep.", recurse_depth); |
| s->EOL(); |
| } |
| } |
| else |
| { |
| s->PutCString(": < EMPTY >\n"); |
| return; |
| } |
| DWARFDebugInfo::Parse(dwarf2Data, DumpCallback, &dumpInfo); |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Dump |
| // |
| // Dump the contents of this DWARFDebugInfo object as has been parsed |
| // and/or modified after it has been parsed. |
| //---------------------------------------------------------------------- |
| void |
| DWARFDebugInfo::Dump (Stream *s, const uint32_t die_offset, const uint32_t recurse_depth) |
| { |
| DumpInfo dumpInfo(s, die_offset, recurse_depth); |
| |
| s->PutCString("Dumping .debug_info section from internal representation\n"); |
| |
| CompileUnitColl::const_iterator pos; |
| uint32_t curr_depth = 0; |
| ParseCompileUnitHeadersIfNeeded(); |
| for (pos = m_compile_units.begin(); pos != m_compile_units.end(); ++pos) |
| { |
| const DWARFCompileUnitSP& cu_sp = *pos; |
| DumpCallback(m_dwarf2Data, (DWARFCompileUnitSP&)cu_sp, NULL, 0, curr_depth, &dumpInfo); |
| cu_sp->DIE()->Dump(m_dwarf2Data, cu_sp.get(), s, recurse_depth); |
| } |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // FindCallbackString |
| // |
| // A callback function for the static DWARFDebugInfo::Parse() function |
| // that gets called each time a compile unit header or debug information |
| // entry is successfully parsed. |
| // |
| // This function will find the die_offset of any items whose DW_AT_name |
| // matches the given string |
| //---------------------------------------------------------------------- |
| typedef struct FindCallbackStringInfoTag |
| { |
| const char* name; |
| bool ignore_case; |
| RegularExpression* regex; |
| vector<dw_offset_t>& die_offsets; |
| } FindCallbackStringInfo; |
| |
| static dw_offset_t FindCallbackString |
| ( |
| SymbolFileDWARF* dwarf2Data, |
| DWARFCompileUnitSP& cu_sp, |
| DWARFDebugInfoEntry* die, |
| const dw_offset_t next_offset, |
| const uint32_t curr_depth, |
| void* userData |
| ) |
| { |
| FindCallbackStringInfo* info = (FindCallbackStringInfo*)userData; |
| const DWARFCompileUnit* cu = cu_sp.get(); |
| |
| if (die) |
| { |
| const char* die_name = die->GetName(dwarf2Data, cu); |
| if (die_name) |
| { |
| if (info->regex) |
| { |
| if (info->regex->Execute(die_name)) |
| info->die_offsets.push_back(die->GetOffset()); |
| } |
| else |
| { |
| if ((info->ignore_case ? strcasecmp(die_name, info->name) : strcmp(die_name, info->name)) == 0) |
| info->die_offsets.push_back(die->GetOffset()); |
| } |
| } |
| } |
| |
| // Just return the current offset to parse the next CU or DIE entry |
| return next_offset; |
| } |
| |
| //---------------------------------------------------------------------- |
| // Find |
| // |
| // Finds all DIE that have a specific DW_AT_name attribute by manually |
| // searching through the debug information (not using the |
| // .debug_pubnames section). The string must match the entire name |
| // and case sensitive searches are an option. |
| //---------------------------------------------------------------------- |
| bool |
| DWARFDebugInfo::Find(const char* name, bool ignore_case, vector<dw_offset_t>& die_offsets) const |
| { |
| die_offsets.clear(); |
| if (name && name[0]) |
| { |
| FindCallbackStringInfo info = { name, ignore_case, NULL, die_offsets }; |
| DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); |
| } |
| return !die_offsets.empty(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Find |
| // |
| // Finds all DIE that have a specific DW_AT_name attribute by manually |
| // searching through the debug information (not using the |
| // .debug_pubnames section). The string must match the supplied regular |
| // expression. |
| //---------------------------------------------------------------------- |
| bool |
| DWARFDebugInfo::Find(RegularExpression& re, vector<dw_offset_t>& die_offsets) const |
| { |
| die_offsets.clear(); |
| FindCallbackStringInfo info = { NULL, false, &re, die_offsets }; |
| DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); |
| return !die_offsets.empty(); |
| } |