mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-02-05 05:13:36 +00:00
- PDBSourceLineWriter (dump_syms) outputs stack frame debugging information - SourceLineResolver reads the new information and puts it into a new StackFrameInfo structure, which is stored in a ContainedRangeMap. FillSourceLineInfo passes the StackFrameInfo back to the caller. - The base Stackwalker makes StackFrameInfo data available to subclasses during stackwalking, but does not use this information directly itself. Stackwalkers may access stack_frame_info_ for enhanced stackwalking (this will be part 4). - New test data for the updated dumped-symbol format http://groups.google.com/group/airbag-dev/browse_thread/thread/735f191c9a1a1de4 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@38 4c0a9323-5329-0410-9bdc-e9ce6186880e
314 lines
8.7 KiB
C++
314 lines
8.7 KiB
C++
// Copyright (c) 2006, 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.
|
|
|
|
#include <stdio.h>
|
|
#include <atlbase.h>
|
|
#include <dia2.h>
|
|
#include "pdb_source_line_writer.h"
|
|
|
|
namespace google_airbag {
|
|
|
|
PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
|
|
}
|
|
|
|
PDBSourceLineWriter::~PDBSourceLineWriter() {
|
|
}
|
|
|
|
bool PDBSourceLineWriter::Open(const wstring &pdb_file) {
|
|
Close();
|
|
|
|
if (FAILED(CoInitialize(NULL))) {
|
|
fprintf(stderr, "CoInitialize failed\n");
|
|
return false;
|
|
}
|
|
|
|
CComPtr<IDiaDataSource> data_source;
|
|
if (FAILED(data_source.CoCreateInstance(CLSID_DiaSource))) {
|
|
fprintf(stderr, "CoCreateInstance CLSID_DiaSource failed "
|
|
"(msdia80.dll unregistered?)\n");
|
|
return false;
|
|
}
|
|
|
|
if (FAILED(data_source->loadDataFromPdb(pdb_file.c_str()))) {
|
|
fprintf(stderr, "loadDataFromPdb failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (FAILED(data_source->openSession(&session_))) {
|
|
fprintf(stderr, "openSession failed\n");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
|
|
// The line number format is:
|
|
// <rva> <line number> <source file id>
|
|
CComPtr<IDiaLineNumber> line;
|
|
ULONG count;
|
|
|
|
while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) {
|
|
DWORD rva;
|
|
if (FAILED(line->get_relativeVirtualAddress(&rva))) {
|
|
fprintf(stderr, "failed to get line rva\n");
|
|
return false;
|
|
}
|
|
|
|
DWORD length;
|
|
if (FAILED(line->get_length(&length))) {
|
|
fprintf(stderr, "failed to get line code length\n");
|
|
return false;
|
|
}
|
|
|
|
DWORD source_id;
|
|
if (FAILED(line->get_sourceFileId(&source_id))) {
|
|
fprintf(stderr, "failed to get line source file id\n");
|
|
return false;
|
|
}
|
|
|
|
DWORD line_num;
|
|
if (FAILED(line->get_lineNumber(&line_num))) {
|
|
fprintf(stderr, "failed to get line number\n");
|
|
return false;
|
|
}
|
|
|
|
fprintf(output_, "%x %x %d %d\n", rva, length, line_num, source_id);
|
|
line.Release();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) {
|
|
// The function format is:
|
|
// FUNC <address> <function>
|
|
CComBSTR name;
|
|
if (FAILED(function->get_name(&name))) {
|
|
fprintf(stderr, "failed to get function name\n");
|
|
return false;
|
|
}
|
|
if (name.Length() == 0) {
|
|
fprintf(stderr, "empty function name\n");
|
|
return false;
|
|
}
|
|
|
|
ULONGLONG length;
|
|
if (FAILED(function->get_length(&length))) {
|
|
fprintf(stderr, "failed to get function length\n");
|
|
return false;
|
|
}
|
|
|
|
DWORD rva;
|
|
if (FAILED(function->get_relativeVirtualAddress(&rva))) {
|
|
fprintf(stderr, "couldn't get rva\n");
|
|
return false;
|
|
}
|
|
|
|
CComPtr<IDiaEnumLineNumbers> lines;
|
|
if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
|
|
return false;
|
|
}
|
|
|
|
fwprintf(output_, L"FUNC %x %llx %s\n", rva, length, name);
|
|
if (!PrintLines(lines)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PDBSourceLineWriter::PrintSourceFiles() {
|
|
CComPtr<IDiaSymbol> global;
|
|
if (FAILED(session_->get_globalScope(&global))) {
|
|
fprintf(stderr, "get_globalScope failed\n");
|
|
return false;
|
|
}
|
|
|
|
CComPtr<IDiaEnumSymbols> compilands;
|
|
if (FAILED(global->findChildren(SymTagCompiland, NULL,
|
|
nsNone, &compilands))) {
|
|
fprintf(stderr, "findChildren failed\n");
|
|
return false;
|
|
}
|
|
|
|
CComPtr<IDiaSymbol> compiland;
|
|
ULONG count;
|
|
while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
|
|
CComPtr<IDiaEnumSourceFiles> source_files;
|
|
if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) {
|
|
return false;
|
|
}
|
|
CComPtr<IDiaSourceFile> file;
|
|
while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) {
|
|
DWORD file_id;
|
|
if (FAILED(file->get_uniqueId(&file_id))) {
|
|
return false;
|
|
}
|
|
|
|
CComBSTR file_name;
|
|
if (FAILED(file->get_fileName(&file_name))) {
|
|
return false;
|
|
}
|
|
|
|
fwprintf(output_, L"FILE %d %s\n", file_id, file_name);
|
|
file.Release();
|
|
}
|
|
compiland.Release();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PDBSourceLineWriter::PrintFunctions() {
|
|
CComPtr<IDiaEnumSymbolsByAddr> symbols;
|
|
if (FAILED(session_->getSymbolsByAddr(&symbols))) {
|
|
fprintf(stderr, "failed to get symbol enumerator\n");
|
|
return false;
|
|
}
|
|
|
|
CComPtr<IDiaSymbol> symbol;
|
|
if (FAILED(symbols->symbolByAddr(1, 0, &symbol))) {
|
|
fprintf(stderr, "failed to enumerate symbols\n");
|
|
return false;
|
|
}
|
|
|
|
DWORD rva_last = 0;
|
|
if (FAILED(symbol->get_relativeVirtualAddress(&rva_last))) {
|
|
fprintf(stderr, "failed to get symbol rva\n");
|
|
return false;
|
|
}
|
|
|
|
ULONG count;
|
|
do {
|
|
DWORD tag;
|
|
if (FAILED(symbol->get_symTag(&tag))) {
|
|
fprintf(stderr, "failed to get symbol tag\n");
|
|
return false;
|
|
}
|
|
if (tag == SymTagFunction) {
|
|
if (!PrintFunction(symbol)) {
|
|
return false;
|
|
}
|
|
}
|
|
symbol.Release();
|
|
} while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PDBSourceLineWriter::PrintFrameData() {
|
|
// It would be nice if it were possible to output frame data alongside the
|
|
// associated function, as is done with line numbers, but the DIA API
|
|
// doesn't make it possible to get the frame data in that way.
|
|
|
|
CComPtr<IDiaEnumTables> tables;
|
|
if (FAILED(session_->getEnumTables(&tables)))
|
|
return false;
|
|
|
|
// Pick up the first table that supports IDiaEnumFrameData.
|
|
CComPtr<IDiaEnumFrameData> frame_data_enum;
|
|
CComPtr<IDiaTable> table;
|
|
ULONG count;
|
|
while (!frame_data_enum &&
|
|
SUCCEEDED(tables->Next(1, &table, &count)) &&
|
|
count == 1) {
|
|
table->QueryInterface(_uuidof(IDiaEnumFrameData),
|
|
reinterpret_cast<void**>(&frame_data_enum));
|
|
table.Release();
|
|
}
|
|
if (!frame_data_enum)
|
|
return false;
|
|
|
|
CComPtr<IDiaFrameData> frame_data;
|
|
while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) &&
|
|
count == 1) {
|
|
DWORD type;
|
|
if (FAILED(frame_data->get_type(&type)))
|
|
return false;
|
|
|
|
DWORD rva;
|
|
if (FAILED(frame_data->get_relativeVirtualAddress(&rva)))
|
|
return false;
|
|
|
|
DWORD code_size;
|
|
if (FAILED(frame_data->get_lengthBlock(&code_size)))
|
|
return false;
|
|
|
|
DWORD prolog_size;
|
|
if (FAILED(frame_data->get_lengthProlog(&prolog_size)))
|
|
return false;
|
|
|
|
// epliog_size is always 0.
|
|
DWORD epilog_size = 0;
|
|
|
|
DWORD parameter_size;
|
|
if (FAILED(frame_data->get_lengthParams(¶meter_size)))
|
|
return false;
|
|
|
|
DWORD saved_register_size;
|
|
if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size)))
|
|
return false;
|
|
|
|
DWORD local_size;
|
|
if (FAILED(frame_data->get_lengthLocals(&local_size)))
|
|
return false;
|
|
|
|
DWORD max_stack_size;
|
|
if (FAILED(frame_data->get_maxStack(&max_stack_size)))
|
|
return false;
|
|
|
|
BSTR program_string;
|
|
if (FAILED(frame_data->get_program(&program_string)))
|
|
return false;
|
|
|
|
fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %ws\n",
|
|
type, rva, code_size, prolog_size, epilog_size,
|
|
parameter_size, saved_register_size, local_size, max_stack_size,
|
|
program_string);
|
|
|
|
frame_data.Release();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
|
|
bool ret = false;
|
|
output_ = map_file;
|
|
if (PrintSourceFiles() && PrintFunctions() && PrintFrameData()) {
|
|
ret = true;
|
|
}
|
|
|
|
output_ = NULL;
|
|
return ret;
|
|
}
|
|
|
|
void PDBSourceLineWriter::Close() {
|
|
session_.Release();
|
|
}
|
|
|
|
} // namespace google_airbag
|