Index: obsolete/breakpad/common/windows/pdb_source_line_writer.cc |
diff --git a/obsolete/breakpad/common/windows/pdb_source_line_writer.cc b/obsolete/breakpad/common/windows/pdb_source_line_writer.cc |
deleted file mode 100644 |
index 9cd0d2e393b15105fcd2d7cce5869abb4cc08501..0000000000000000000000000000000000000000 |
--- a/obsolete/breakpad/common/windows/pdb_source_line_writer.cc |
+++ /dev/null |
@@ -1,870 +0,0 @@ |
-// 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 <atlbase.h> |
-#include <DbgHelp.h> |
-#include <dia2.h> |
-#include <stdio.h> |
- |
-#include "common/windows/string_utils-inl.h" |
- |
-#include "common/windows/pdb_source_line_writer.h" |
-#include "common/windows/guid_string.h" |
- |
-// This constant may be missing from DbgHelp.h. See the documentation for |
-// IDiaSymbol::get_undecoratedNameEx. |
-#ifndef UNDNAME_NO_ECSU |
-#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union. |
-#endif // UNDNAME_NO_ECSU |
- |
-namespace google_breakpad { |
- |
-PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { |
-} |
- |
-PDBSourceLineWriter::~PDBSourceLineWriter() { |
-} |
- |
-bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { |
- 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; |
- } |
- |
- switch (format) { |
- case PDB_FILE: |
- if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { |
- fprintf(stderr, "loadDataFromPdb failed\n"); |
- return false; |
- } |
- break; |
- case EXE_FILE: |
- if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { |
- fprintf(stderr, "loadDataForExe failed\n"); |
- return false; |
- } |
- break; |
- case ANY_FILE: |
- if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { |
- if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { |
- fprintf(stderr, "loadDataForPdb and loadDataFromExe failed\n"); |
- return false; |
- } |
- } |
- break; |
- default: |
- fprintf(stderr, "Unknown file format\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 dia_source_id; |
- if (FAILED(line->get_sourceFileId(&dia_source_id))) { |
- fprintf(stderr, "failed to get line source file id\n"); |
- return false; |
- } |
- // duplicate file names are coalesced to share one ID |
- DWORD source_id = GetRealFileID(dia_source_id); |
- |
- 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, |
- IDiaSymbol *block) { |
- // The function format is: |
- // FUNC <address> <length> <param_stack_size> <function> |
- DWORD rva; |
- if (FAILED(block->get_relativeVirtualAddress(&rva))) { |
- fprintf(stderr, "couldn't get rva\n"); |
- return false; |
- } |
- |
- ULONGLONG length; |
- if (FAILED(block->get_length(&length))) { |
- fprintf(stderr, "failed to get function length\n"); |
- return false; |
- } |
- |
- if (length == 0) { |
- // Silently ignore zero-length functions, which can infrequently pop up. |
- return true; |
- } |
- |
- CComBSTR name; |
- int stack_param_size; |
- if (!GetSymbolFunctionName(function, &name, &stack_param_size)) { |
- return false; |
- } |
- |
- // If the decorated name didn't give the parameter size, try to |
- // calculate it. |
- if (stack_param_size < 0) { |
- stack_param_size = GetFunctionStackParamSize(function); |
- } |
- |
- fprintf(output_, "FUNC %x %" WIN_STRING_FORMAT_LL "x %x %ws\n", |
- rva, length, stack_param_size, name); |
- |
- CComPtr<IDiaEnumLineNumbers> lines; |
- if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) { |
- return false; |
- } |
- |
- 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; |
- } |
- |
- wstring file_name_string(file_name); |
- if (!FileIDIsCached(file_name_string)) { |
- // this is a new file name, cache it and output a FILE line. |
- CacheFileID(file_name_string, file_id); |
- fwprintf(output_, L"FILE %d %s\n", file_id, file_name); |
- } else { |
- // this file name has already been seen, just save this |
- // ID for later lookup. |
- StoreDuplicateFileID(file_name_string, file_id); |
- } |
- 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; |
- } |
- |
- // For a given function, DIA seems to give either a symbol with |
- // SymTagFunction or SymTagPublicSymbol, but not both. This means |
- // that PDBSourceLineWriter will output either a FUNC or PUBLIC line, |
- // but not both. |
- if (tag == SymTagFunction) { |
- if (!PrintFunction(symbol, symbol)) { |
- return false; |
- } |
- } else if (tag == SymTagPublicSymbol) { |
- if (!PrintCodePublicSymbol(symbol)) { |
- return false; |
- } |
- } |
- symbol.Release(); |
- } while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1); |
- |
- // When building with PGO, the compiler can split functions into |
- // "hot" and "cold" blocks, and move the "cold" blocks out to separate |
- // pages, so the function can be noncontiguous. To find these blocks, |
- // we have to iterate over all the compilands, and then find blocks |
- // that are children of them. We can then find the lexical parents |
- // of those blocks and print out an extra FUNC line for blocks |
- // that are not contained in their parent functions. |
- 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 on the global\n"); |
- return false; |
- } |
- |
- CComPtr<IDiaSymbol> compiland; |
- while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { |
- CComPtr<IDiaEnumSymbols> blocks; |
- if (FAILED(compiland->findChildren(SymTagBlock, NULL, |
- nsNone, &blocks))) { |
- fprintf(stderr, "findChildren failed on a compiland\n"); |
- return false; |
- } |
- |
- CComPtr<IDiaSymbol> block; |
- while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { |
- // find this block's lexical parent function |
- CComPtr<IDiaSymbol> parent; |
- DWORD tag; |
- if (SUCCEEDED(block->get_lexicalParent(&parent)) && |
- SUCCEEDED(parent->get_symTag(&tag)) && |
- tag == SymTagFunction) { |
- // now get the block's offset and the function's offset and size, |
- // and determine if the block is outside of the function |
- DWORD func_rva, block_rva; |
- ULONGLONG func_length; |
- if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && |
- SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && |
- SUCCEEDED(parent->get_length(&func_length))) { |
- if (block_rva < func_rva || block_rva > (func_rva + func_length)) { |
- if (!PrintFunction(parent, block)) { |
- return false; |
- } |
- } |
- } |
- } |
- parent.Release(); |
- block.Release(); |
- } |
- blocks.Release(); |
- compiland.Release(); |
- } |
- |
- 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; |
- |
- DWORD last_type = -1; |
- DWORD last_rva = -1; |
- DWORD last_code_size = 0; |
- DWORD last_prolog_size = -1; |
- |
- 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; |
- |
- // parameter_size is the size of parameters passed on the stack. If any |
- // parameters are not passed on the stack (such as in registers), their |
- // sizes will not be included in parameter_size. |
- 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; |
- |
- // get_maxStack can return S_FALSE, just use 0 in that case. |
- DWORD max_stack_size = 0; |
- if (FAILED(frame_data->get_maxStack(&max_stack_size))) |
- return false; |
- |
- // get_programString can return S_FALSE, indicating that there is no |
- // program string. In that case, check whether %ebp is used. |
- HRESULT program_string_result; |
- CComBSTR program_string; |
- if (FAILED(program_string_result = frame_data->get_program( |
- &program_string))) { |
- return false; |
- } |
- |
- // get_allocatesBasePointer can return S_FALSE, treat that as though |
- // %ebp is not used. |
- BOOL allocates_base_pointer = FALSE; |
- if (program_string_result != S_OK) { |
- if (FAILED(frame_data->get_allocatesBasePointer( |
- &allocates_base_pointer))) { |
- return false; |
- } |
- } |
- |
- // Only print out a line if type, rva, code_size, or prolog_size have |
- // changed from the last line. It is surprisingly common (especially in |
- // system library PDBs) for DIA to return a series of identical |
- // IDiaFrameData objects. For kernel32.pdb from Windows XP SP2 on x86, |
- // this check reduces the size of the dumped symbol file by a third. |
- if (type != last_type || rva != last_rva || code_size != last_code_size || |
- prolog_size != last_prolog_size) { |
- fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %d ", |
- type, rva, code_size, prolog_size, epilog_size, |
- parameter_size, saved_register_size, local_size, max_stack_size, |
- program_string_result == S_OK); |
- if (program_string_result == S_OK) { |
- fprintf(output_, "%ws\n", program_string); |
- } else { |
- fprintf(output_, "%d\n", allocates_base_pointer); |
- } |
- |
- last_type = type; |
- last_rva = rva; |
- last_code_size = code_size; |
- last_prolog_size = prolog_size; |
- } |
- |
- frame_data.Release(); |
- } |
- |
- return true; |
-} |
- |
-bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) { |
- BOOL is_code; |
- if (FAILED(symbol->get_code(&is_code))) { |
- return false; |
- } |
- if (!is_code) { |
- return true; |
- } |
- |
- DWORD rva; |
- if (FAILED(symbol->get_relativeVirtualAddress(&rva))) { |
- return false; |
- } |
- |
- CComBSTR name; |
- int stack_param_size; |
- if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) { |
- return false; |
- } |
- |
- fprintf(output_, "PUBLIC %x %x %ws\n", rva, |
- stack_param_size > 0 ? stack_param_size : 0, name); |
- return true; |
-} |
- |
-bool PDBSourceLineWriter::PrintPDBInfo() { |
- PDBModuleInfo info; |
- if (!GetModuleInfo(&info)) { |
- return false; |
- } |
- |
- // Hard-code "windows" for the OS because that's the only thing that makes |
- // sense for PDB files. (This might not be strictly correct for Windows CE |
- // support, but we don't care about that at the moment.) |
- fprintf(output_, "MODULE windows %ws %ws %ws\n", |
- info.cpu.c_str(), info.debug_identifier.c_str(), |
- info.debug_file.c_str()); |
- |
- return true; |
-} |
- |
-// wcstol_positive_strict is sort of like wcstol, but much stricter. string |
-// should be a buffer pointing to a null-terminated string containing only |
-// decimal digits. If the entire string can be converted to an integer |
-// without overflowing, and there are no non-digit characters before the |
-// result is set to the value and this function returns true. Otherwise, |
-// this function returns false. This is an alternative to the strtol, atoi, |
-// and scanf families, which are not as strict about input and in some cases |
-// don't provide a good way for the caller to determine if a conversion was |
-// successful. |
-static bool wcstol_positive_strict(wchar_t *string, int *result) { |
- int value = 0; |
- for (wchar_t *c = string; *c != '\0'; ++c) { |
- int last_value = value; |
- value *= 10; |
- // Detect overflow. |
- if (value / 10 != last_value || value < 0) { |
- return false; |
- } |
- if (*c < '0' || *c > '9') { |
- return false; |
- } |
- unsigned int c_value = *c - '0'; |
- last_value = value; |
- value += c_value; |
- // Detect overflow. |
- if (value < last_value) { |
- return false; |
- } |
- // Forbid leading zeroes unless the string is just "0". |
- if (value == 0 && *(c+1) != '\0') { |
- return false; |
- } |
- } |
- *result = value; |
- return true; |
-} |
- |
-// static |
-bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, |
- BSTR *name, |
- int *stack_param_size) { |
- *stack_param_size = -1; |
- const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS | |
- UNDNAME_NO_FUNCTION_RETURNS | |
- UNDNAME_NO_ALLOCATION_MODEL | |
- UNDNAME_NO_ALLOCATION_LANGUAGE | |
- UNDNAME_NO_THISTYPE | |
- UNDNAME_NO_ACCESS_SPECIFIERS | |
- UNDNAME_NO_THROW_SIGNATURES | |
- UNDNAME_NO_MEMBER_TYPE | |
- UNDNAME_NO_RETURN_UDT_MODEL | |
- UNDNAME_NO_ECSU; |
- |
- // Use get_undecoratedNameEx to get readable C++ names with arguments. |
- if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) { |
- if (function->get_name(name) != S_OK) { |
- fprintf(stderr, "failed to get function name\n"); |
- return false; |
- } |
- // If a name comes from get_name because no undecorated form existed, |
- // it's already formatted properly to be used as output. Don't do any |
- // additional processing. |
- // |
- // MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's. |
- // This will result in calling get_name for some C++ symbols, so |
- // all of the parameter and return type information may not be included in |
- // the name string. |
- } else { |
- // C++ uses a bogus "void" argument for functions and methods that don't |
- // take any parameters. Take it out of the undecorated name because it's |
- // ugly and unnecessary. |
- const wchar_t *replace_string = L"(void)"; |
- const size_t replace_length = wcslen(replace_string); |
- const wchar_t *replacement_string = L"()"; |
- size_t length = wcslen(*name); |
- if (length >= replace_length) { |
- wchar_t *name_end = *name + length - replace_length; |
- if (wcscmp(name_end, replace_string) == 0) { |
- WindowsStringUtils::safe_wcscpy(name_end, replace_length, |
- replacement_string); |
- length = wcslen(*name); |
- } |
- } |
- |
- // Undecorate names used for stdcall and fastcall. These names prefix |
- // the identifier with '_' (stdcall) or '@' (fastcall) and suffix it |
- // with '@' followed by the number of bytes of parameters, in decimal. |
- // If such a name is found, take note of the size and undecorate it. |
- // Only do this for names that aren't C++, which is determined based on |
- // whether the undecorated name contains any ':' or '(' characters. |
- if (!wcschr(*name, ':') && !wcschr(*name, '(') && |
- (*name[0] == '_' || *name[0] == '@')) { |
- wchar_t *last_at = wcsrchr(*name + 1, '@'); |
- if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) { |
- // If this function adheres to the fastcall convention, it accepts up |
- // to the first 8 bytes of parameters in registers (%ecx and %edx). |
- // We're only interested in the stack space used for parameters, so |
- // so subtract 8 and don't let the size go below 0. |
- if (*name[0] == '@') { |
- if (*stack_param_size > 8) { |
- *stack_param_size -= 8; |
- } else { |
- *stack_param_size = 0; |
- } |
- } |
- |
- // Undecorate the name by moving it one character to the left in its |
- // buffer, and terminating it where the last '@' had been. |
- WindowsStringUtils::safe_wcsncpy(*name, length, |
- *name + 1, last_at - *name - 1); |
- } else if (*name[0] == '_') { |
- // This symbol's name is encoded according to the cdecl rules. The |
- // name doesn't end in a '@' character followed by a decimal positive |
- // integer, so it's not a stdcall name. Strip off the leading |
- // underscore. |
- WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length); |
- } |
- } |
- } |
- |
- return true; |
-} |
- |
-// static |
-int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) { |
- // This implementation is highly x86-specific. |
- |
- // Gather the symbols corresponding to data. |
- CComPtr<IDiaEnumSymbols> data_children; |
- if (FAILED(function->findChildren(SymTagData, NULL, nsNone, |
- &data_children))) { |
- return 0; |
- } |
- |
- // lowest_base is the lowest %ebp-relative byte offset used for a parameter. |
- // highest_end is one greater than the highest offset (i.e. base + length). |
- // Stack parameters are assumed to be contiguous, because in reality, they |
- // are. |
- int lowest_base = INT_MAX; |
- int highest_end = INT_MIN; |
- |
- CComPtr<IDiaSymbol> child; |
- DWORD count; |
- while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) { |
- // If any operation fails at this point, just proceed to the next child. |
- // Use the next_child label instead of continue because child needs to |
- // be released before it's reused. Declare constructable/destructable |
- // types early to avoid gotos that cross initializations. |
- CComPtr<IDiaSymbol> child_type; |
- |
- // DataIsObjectPtr is only used for |this|. Because |this| can be passed |
- // as a stack parameter, look for it in addition to traditional |
- // parameters. |
- DWORD child_kind; |
- if (FAILED(child->get_dataKind(&child_kind)) || |
- (child_kind != DataIsParam && child_kind != DataIsObjectPtr)) { |
- goto next_child; |
- } |
- |
- // Only concentrate on register-relative parameters. Parameters may also |
- // be enregistered (passed directly in a register), but those don't |
- // consume any stack space, so they're not of interest. |
- DWORD child_location_type; |
- if (FAILED(child->get_locationType(&child_location_type)) || |
- child_location_type != LocIsRegRel) { |
- goto next_child; |
- } |
- |
- // Of register-relative parameters, the only ones that make any sense are |
- // %ebp- or %esp-relative. Note that MSVC's debugging information always |
- // gives parameters as %ebp-relative even when a function doesn't use a |
- // traditional frame pointer and stack parameters are accessed relative to |
- // %esp, so just look for %ebp-relative parameters. If you wanted to |
- // access parameters, you'd probably want to treat these %ebp-relative |
- // offsets as if they were relative to %esp before a function's prolog |
- // executed. |
- DWORD child_register; |
- if (FAILED(child->get_registerId(&child_register)) || |
- child_register != CV_REG_EBP) { |
- goto next_child; |
- } |
- |
- LONG child_register_offset; |
- if (FAILED(child->get_offset(&child_register_offset))) { |
- goto next_child; |
- } |
- |
- // IDiaSymbol::get_type can succeed but still pass back a NULL value. |
- if (FAILED(child->get_type(&child_type)) || !child_type) { |
- goto next_child; |
- } |
- |
- ULONGLONG child_length; |
- if (FAILED(child_type->get_length(&child_length))) { |
- goto next_child; |
- } |
- |
- int child_end = child_register_offset + static_cast<ULONG>(child_length); |
- if (child_register_offset < lowest_base) { |
- lowest_base = child_register_offset; |
- } |
- if (child_end > highest_end) { |
- highest_end = child_end; |
- } |
- |
-next_child: |
- child.Release(); |
- } |
- |
- int param_size = 0; |
- // Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest |
- // possible address to find a stack parameter before executing a function's |
- // prolog (see above). Some optimizations cause parameter offsets to be |
- // lower than 4, but we're not concerned with those because we're only |
- // looking for parameters contained in addresses higher than where the |
- // return address is stored. |
- if (lowest_base < 4) { |
- lowest_base = 4; |
- } |
- if (highest_end > lowest_base) { |
- // All stack parameters are pushed as at least 4-byte quantities. If the |
- // last type was narrower than 4 bytes, promote it. This assumes that all |
- // parameters' offsets are 4-byte-aligned, which is always the case. Only |
- // worry about the last type, because we're not summing the type sizes, |
- // just looking at the lowest and highest offsets. |
- int remainder = highest_end % 4; |
- if (remainder) { |
- highest_end += 4 - remainder; |
- } |
- |
- param_size = highest_end - lowest_base; |
- } |
- |
- return param_size; |
-} |
- |
-bool PDBSourceLineWriter::WriteMap(FILE *map_file) { |
- output_ = map_file; |
- |
- bool ret = PrintPDBInfo() && |
- PrintSourceFiles() && |
- PrintFunctions() && |
- PrintFrameData(); |
- |
- output_ = NULL; |
- return ret; |
-} |
- |
-void PDBSourceLineWriter::Close() { |
- session_.Release(); |
-} |
- |
-bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { |
- if (!info) { |
- return false; |
- } |
- |
- info->debug_file.clear(); |
- info->debug_identifier.clear(); |
- info->cpu.clear(); |
- |
- CComPtr<IDiaSymbol> global; |
- if (FAILED(session_->get_globalScope(&global))) { |
- return false; |
- } |
- |
- // All CPUs in CV_CPU_TYPE_e (cvconst.h) below 0x10 are x86. There's no |
- // single specific constant to use. |
- DWORD platform; |
- if (SUCCEEDED(global->get_platform(&platform)) && platform < 0x10) { |
- info->cpu = L"x86"; |
- } else { |
- // Unexpected, but handle gracefully. |
- info->cpu = L"unknown"; |
- } |
- |
- // DWORD* and int* are not compatible. This is clean and avoids a cast. |
- DWORD age; |
- if (FAILED(global->get_age(&age))) { |
- return false; |
- } |
- |
- bool uses_guid; |
- if (!UsesGUID(&uses_guid)) { |
- return false; |
- } |
- |
- if (uses_guid) { |
- GUID guid; |
- if (FAILED(global->get_guid(&guid))) { |
- return false; |
- } |
- |
- // Use the same format that the MS symbol server uses in filesystem |
- // hierarchies. |
- wchar_t age_string[9]; |
- swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]), |
- L"%x", age); |
- |
- // remove when VC++7.1 is no longer supported |
- age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0'; |
- |
- info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid); |
- info->debug_identifier.append(age_string); |
- } else { |
- DWORD signature; |
- if (FAILED(global->get_signature(&signature))) { |
- return false; |
- } |
- |
- // Use the same format that the MS symbol server uses in filesystem |
- // hierarchies. |
- wchar_t identifier_string[17]; |
- swprintf(identifier_string, |
- sizeof(identifier_string) / sizeof(identifier_string[0]), |
- L"%08X%x", signature, age); |
- |
- // remove when VC++7.1 is no longer supported |
- identifier_string[sizeof(identifier_string) / |
- sizeof(identifier_string[0]) - 1] = L'\0'; |
- |
- info->debug_identifier = identifier_string; |
- } |
- |
- CComBSTR debug_file_string; |
- if (FAILED(global->get_symbolsFileName(&debug_file_string))) { |
- return false; |
- } |
- info->debug_file = |
- WindowsStringUtils::GetBaseName(wstring(debug_file_string)); |
- |
- return true; |
-} |
- |
-bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { |
- if (!uses_guid) |
- return false; |
- |
- CComPtr<IDiaSymbol> global; |
- if (FAILED(session_->get_globalScope(&global))) |
- return false; |
- |
- GUID guid; |
- if (FAILED(global->get_guid(&guid))) |
- return false; |
- |
- DWORD signature; |
- if (FAILED(global->get_signature(&signature))) |
- return false; |
- |
- // There are two possibilities for guid: either it's a real 128-bit GUID |
- // as identified in a code module by a new-style CodeView record, or it's |
- // a 32-bit signature (timestamp) as identified by an old-style record. |
- // See MDCVInfoPDB70 and MDCVInfoPDB20 in minidump_format.h. |
- // |
- // Because DIA doesn't provide a way to directly determine whether a module |
- // uses a GUID or a 32-bit signature, this code checks whether the first 32 |
- // bits of guid are the same as the signature, and if the rest of guid is |
- // zero. If so, then with a pretty high degree of certainty, there's an |
- // old-style CodeView record in use. This method will only falsely find an |
- // an old-style CodeView record if a real 128-bit GUID has its first 32 |
- // bits set the same as the module's signature (timestamp) and the rest of |
- // the GUID is set to 0. This is highly unlikely. |
- |
- GUID signature_guid = {signature}; // 0-initializes other members |
- *uses_guid = !IsEqualGUID(guid, signature_guid); |
- return true; |
-} |
- |
-} // namespace google_breakpad |