| // Copyright (c) 2010 Google Inc. All Rights Reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // This is a client for the dwarf2reader to extract function and line |
| // information from the debug info. |
| |
| #include <assert.h> |
| #include <limits.h> |
| #include <stdio.h> |
| |
| #include <map> |
| #include <queue> |
| #include <vector> |
| |
| #include "common/dwarf/functioninfo.h" |
| #include "common/dwarf/bytereader.h" |
| #include "common/scoped_ptr.h" |
| #include "common/using_std_string.h" |
| |
| using google_breakpad::scoped_ptr; |
| |
| namespace dwarf2reader { |
| |
| CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files, |
| std::vector<string>* dirs, |
| LineMap* linemap):linemap_(linemap), |
| files_(files), |
| dirs_(dirs) { |
| // The dirs and files are 1 indexed, so just make sure we put |
| // nothing in the 0 vector. |
| assert(dirs->size() == 0); |
| assert(files->size() == 0); |
| dirs->push_back(""); |
| SourceFileInfo s; |
| s.name = ""; |
| s.lowpc = ULLONG_MAX; |
| files->push_back(s); |
| } |
| |
| void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { |
| // These should never come out of order, actually |
| assert(dir_num == dirs_->size()); |
| dirs_->push_back(name); |
| } |
| |
| void CULineInfoHandler::DefineFile(const string& name, |
| int32 file_num, uint32 dir_num, |
| uint64 mod_time, uint64 length) { |
| assert(dir_num >= 0); |
| assert(dir_num < dirs_->size()); |
| |
| // These should never come out of order, actually. |
| if (file_num == (int32)files_->size() || file_num == -1) { |
| string dir = dirs_->at(dir_num); |
| |
| SourceFileInfo s; |
| s.lowpc = ULLONG_MAX; |
| |
| if (dir == "") { |
| s.name = name; |
| } else { |
| s.name = dir + "/" + name; |
| } |
| |
| files_->push_back(s); |
| } else { |
| fprintf(stderr, "error in DefineFile"); |
| } |
| } |
| |
| void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, |
| uint32 line_num, uint32 column_num) { |
| if (file_num < files_->size()) { |
| linemap_->insert( |
| std::make_pair(address, |
| std::make_pair(files_->at(file_num).name.c_str(), |
| line_num))); |
| |
| if (address < files_->at(file_num).lowpc) { |
| files_->at(file_num).lowpc = address; |
| } |
| } else { |
| fprintf(stderr, "error in AddLine"); |
| } |
| } |
| |
| bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, |
| uint8 address_size, |
| uint8 offset_size, |
| uint64 cu_length, |
| uint8 dwarf_version) { |
| current_compilation_unit_offset_ = offset; |
| return true; |
| } |
| |
| |
| // For function info, we only care about subprograms and inlined |
| // subroutines. For line info, the DW_AT_stmt_list lives in the |
| // compile unit tag. |
| |
| bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) { |
| switch (tag) { |
| case DW_TAG_subprogram: |
| case DW_TAG_inlined_subroutine: { |
| current_function_info_ = new FunctionInfo; |
| current_function_info_->lowpc = current_function_info_->highpc = 0; |
| current_function_info_->name = ""; |
| current_function_info_->line = 0; |
| current_function_info_->file = ""; |
| offset_to_funcinfo_->insert(std::make_pair(offset, |
| current_function_info_)); |
| }; |
| // FALLTHROUGH |
| case DW_TAG_compile_unit: |
| return true; |
| default: |
| return false; |
| } |
| return false; |
| } |
| |
| // Only care about the name attribute for functions |
| |
| void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, |
| enum DwarfAttribute attr, |
| enum DwarfForm form, |
| const string &data) { |
| if (current_function_info_) { |
| if (attr == DW_AT_name) |
| current_function_info_->name = data; |
| else if (attr == DW_AT_MIPS_linkage_name) |
| current_function_info_->mangled_name = data; |
| } |
| } |
| |
| void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, |
| enum DwarfAttribute attr, |
| enum DwarfForm form, |
| uint64 data) { |
| if (attr == DW_AT_stmt_list) { |
| SectionMap::const_iterator iter = sections_.find("__debug_line"); |
| assert(iter != sections_.end()); |
| |
| scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data, |
| iter->second.second - data, |
| reader_, linehandler_)); |
| lireader->Start(); |
| } else if (current_function_info_) { |
| switch (attr) { |
| case DW_AT_low_pc: |
| current_function_info_->lowpc = data; |
| break; |
| case DW_AT_high_pc: |
| current_function_info_->highpc = data; |
| break; |
| case DW_AT_decl_line: |
| current_function_info_->line = data; |
| break; |
| case DW_AT_decl_file: |
| current_function_info_->file = files_->at(data).name; |
| break; |
| case DW_AT_ranges: |
| current_function_info_->ranges = data; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, |
| enum DwarfAttribute attr, |
| enum DwarfForm form, |
| uint64 data) { |
| if (current_function_info_) { |
| switch (attr) { |
| case DW_AT_specification: { |
| // Some functions have a "specification" attribute |
| // which means they were defined elsewhere. The name |
| // attribute is not repeated, and must be taken from |
| // the specification DIE. Here we'll assume that |
| // any DIE referenced in this manner will already have |
| // been seen, but that's not really required by the spec. |
| FunctionMap::iterator iter = offset_to_funcinfo_->find(data); |
| if (iter != offset_to_funcinfo_->end()) { |
| current_function_info_->name = iter->second->name; |
| current_function_info_->mangled_name = iter->second->mangled_name; |
| } else { |
| // If you hit this, this code probably needs to be rewritten. |
| fprintf(stderr, |
| "Error: DW_AT_specification was seen before the referenced " |
| "DIE! (Looking for DIE at offset %08llx, in DIE at " |
| "offset %08llx)\n", data, offset); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } |
| |
| void CUFunctionInfoHandler::EndDIE(uint64 offset) { |
| if (current_function_info_ && current_function_info_->lowpc) |
| address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, |
| current_function_info_)); |
| } |
| |
| } // namespace dwarf2reader |