mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-07-23 14:38:20 +00:00
Add optional field indicating multiple symbols at an address
Adds an optional 'm' as the first field in FUNCTION and PUBLIC records to indicate that the address corresponds to more than one symbol. Controls this by a command line flag for now to give symbol file users a chance to update. Also reduces the number of IDiaSymbols retained in memory to one per address. This reduces memory consumption by 8% when processing chrome.dll.pdb. Updates the processor to parse the new optional field. Bug: google-breakpad:751 Change-Id: I6503edaf057312d21a1d63d9c84e5a4fa019dc46 Reviewed-on: https://chromium-review.googlesource.com/773418 Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
4eeb384f3e
commit
b1226959a2
|
@ -100,12 +100,17 @@ the file; it may contain spaces.
|
||||||
|
|
||||||
A `FUNC` record describes a source-language function. It has the form:
|
A `FUNC` record describes a source-language function. It has the form:
|
||||||
|
|
||||||
> `FUNC` _address_ _size_ _parameter\_size_ _name_
|
> `FUNC` _[m]_ _address_ _size_ _parameter\_size_ _name_
|
||||||
|
|
||||||
For example: `FUNC c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&,
|
For example: `FUNC m c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&,
|
||||||
void**) const
|
void**) const
|
||||||
`
|
`
|
||||||
|
|
||||||
|
The _m_ field is optional. If present it indicates that multiple symbols
|
||||||
|
reference this function's instructions. (In which case, only one symbol name is
|
||||||
|
mentioned within the breakpad file.) Multiple symbols referencing the same
|
||||||
|
instructions may occur due to identical code folding by the linker.
|
||||||
|
|
||||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||||
address and length in bytes of the machine code instructions the function
|
address and length in bytes of the machine code instructions the function
|
||||||
occupies. (Breakpad symbol files cannot accurately describe functions whose code
|
occupies. (Breakpad symbol files cannot accurately describe functions whose code
|
||||||
|
@ -158,9 +163,9 @@ A `PUBLIC` record describes a publicly visible linker symbol, such as that used
|
||||||
to identify an assembly language entry point or region of memory. It has the
|
to identify an assembly language entry point or region of memory. It has the
|
||||||
form:
|
form:
|
||||||
|
|
||||||
> PUBLIC _address_ _parameter\_size_ _name_
|
> PUBLIC _[m]_ _address_ _parameter\_size_ _name_
|
||||||
|
|
||||||
For example: `PUBLIC 2160 0 Public2_1
|
For example: `PUBLIC m 2160 0 Public2_1
|
||||||
`
|
`
|
||||||
|
|
||||||
The Breakpad processor essentially treats a `PUBLIC` record as defining a
|
The Breakpad processor essentially treats a `PUBLIC` record as defining a
|
||||||
|
@ -168,6 +173,11 @@ function with no line number data and an indeterminate size: the code extends to
|
||||||
the next address mentioned. If a given address is covered by both a `PUBLIC`
|
the next address mentioned. If a given address is covered by both a `PUBLIC`
|
||||||
record and a `FUNC` record, the processor uses the `FUNC` data.
|
record and a `FUNC` record, the processor uses the `FUNC` data.
|
||||||
|
|
||||||
|
The _m_ field is optional. If present it indicates that multiple symbols
|
||||||
|
reference this function's instructions. (In which case, only one symbol name is
|
||||||
|
mentioned within the breakpad file.) Multiple symbols referencing the same
|
||||||
|
instructions may occur due to identical code folding by the linker.
|
||||||
|
|
||||||
The _address_ field is a hexadecimal number indicating the symbol's address,
|
The _address_ field is a hexadecimal number indicating the symbol's address,
|
||||||
relative to the module's load address.
|
relative to the module's load address.
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,53 @@ namespace {
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
typedef std::multimap<DWORD, CComPtr<IDiaSymbol>> SymbolMultimap;
|
// The symbol (among possibly many) selected to represent an rva.
|
||||||
|
struct SelectedSymbol {
|
||||||
|
SelectedSymbol(const CComPtr<IDiaSymbol>& symbol, bool is_public)
|
||||||
|
: symbol(symbol), is_public(is_public), is_multiple(false) {}
|
||||||
|
|
||||||
|
// The symbol to use for an rva.
|
||||||
|
CComPtr<IDiaSymbol> symbol;
|
||||||
|
// Whether this is a public or function symbol.
|
||||||
|
bool is_public;
|
||||||
|
// Whether the rva has multiple associated symbols. An rva will correspond to
|
||||||
|
// multiple symbols in the case of linker identical symbol folding.
|
||||||
|
bool is_multiple;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps rva to the symbol to use for that address.
|
||||||
|
typedef std::map<DWORD, SelectedSymbol> SymbolMap;
|
||||||
|
|
||||||
|
// Record this in the map as the selected symbol for the rva if it satisfies the
|
||||||
|
// necessary conditions.
|
||||||
|
void MaybeRecordSymbol(DWORD rva,
|
||||||
|
const CComPtr<IDiaSymbol> symbol,
|
||||||
|
bool is_public,
|
||||||
|
SymbolMap* map) {
|
||||||
|
SymbolMap::iterator loc = map->find(rva);
|
||||||
|
if (loc == map->end()) {
|
||||||
|
map->insert(std::make_pair(rva, SelectedSymbol(symbol, is_public)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer function symbols to public symbols.
|
||||||
|
if (is_public && !loc->second.is_public) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loc->second.is_multiple = true;
|
||||||
|
|
||||||
|
// Take the 'least' symbol by lexicographical order of the decorated name. We
|
||||||
|
// use the decorated rather than undecorated name because computing the latter
|
||||||
|
// is expensive.
|
||||||
|
BSTR current_name, new_name;
|
||||||
|
loc->second.symbol->get_name(¤t_name);
|
||||||
|
symbol->get_name(&new_name);
|
||||||
|
if (wcscmp(new_name, current_name) < 0) {
|
||||||
|
loc->second.symbol = symbol;
|
||||||
|
loc->second.is_public = is_public;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A helper class to scope a PLOADED_IMAGE.
|
// A helper class to scope a PLOADED_IMAGE.
|
||||||
class AutoImage {
|
class AutoImage {
|
||||||
|
@ -171,19 +217,10 @@ bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computing undecorated names for all symbols is expensive, so we compare
|
|
||||||
// decorated names.
|
|
||||||
bool CompareSymbols(const SymbolMultimap::value_type& a,
|
|
||||||
const SymbolMultimap::value_type& b) {
|
|
||||||
BSTR a_name, b_name;
|
|
||||||
a.second->get_name(&a_name);
|
|
||||||
b.second->get_name(&b_name);
|
|
||||||
return wcscmp(a_name, b_name) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
|
PDBSourceLineWriter::PDBSourceLineWriter(bool enable_multiple_field)
|
||||||
|
: enable_multiple_field_(enable_multiple_field), output_(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PDBSourceLineWriter::~PDBSourceLineWriter() {
|
PDBSourceLineWriter::~PDBSourceLineWriter() {
|
||||||
|
@ -299,7 +336,8 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
|
bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
|
||||||
IDiaSymbol *block) {
|
IDiaSymbol *block,
|
||||||
|
bool has_multiple_symbols) {
|
||||||
// The function format is:
|
// The function format is:
|
||||||
// FUNC <address> <length> <param_stack_size> <function>
|
// FUNC <address> <length> <param_stack_size> <function>
|
||||||
DWORD rva;
|
DWORD rva;
|
||||||
|
@ -335,9 +373,10 @@ bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
|
||||||
MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)),
|
MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)),
|
||||||
&ranges);
|
&ranges);
|
||||||
for (size_t i = 0; i < ranges.size(); ++i) {
|
for (size_t i = 0; i < ranges.size(); ++i) {
|
||||||
fprintf(output_, "FUNC %lx %lx %x %ws\n",
|
const char* optional_multiple_field =
|
||||||
ranges[i].rva, ranges[i].length, stack_param_size,
|
enable_multiple_field_ && has_multiple_symbols ? "m " : "";
|
||||||
name.m_str);
|
fprintf(output_, "FUNC %s%lx %lx %x %ws\n", optional_multiple_field,
|
||||||
|
ranges[i].rva, ranges[i].length, stack_param_size, name.m_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IDiaEnumLineNumbers> lines;
|
CComPtr<IDiaEnumLineNumbers> lines;
|
||||||
|
@ -415,7 +454,7 @@ bool PDBSourceLineWriter::PrintFunctions() {
|
||||||
CComPtr<IDiaEnumSymbols> symbols = NULL;
|
CComPtr<IDiaEnumSymbols> symbols = NULL;
|
||||||
|
|
||||||
// Find all function symbols first.
|
// Find all function symbols first.
|
||||||
SymbolMultimap rva_symbols;
|
SymbolMap rva_symbol;
|
||||||
hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);
|
hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
|
@ -423,10 +462,8 @@ bool PDBSourceLineWriter::PrintFunctions() {
|
||||||
|
|
||||||
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
|
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
|
||||||
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
|
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
|
||||||
// Place the symbols into a multimap indexed by rva, so we can choose
|
// Potentially record this as the canonical symbol for this rva.
|
||||||
// the apropriate symbol name to use when multiple symbols share an
|
MaybeRecordSymbol(rva, symbol, false, &rva_symbol);
|
||||||
// address.
|
|
||||||
rva_symbols.insert(std::make_pair(rva, symbol));
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
|
fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
|
||||||
return false;
|
return false;
|
||||||
|
@ -438,9 +475,8 @@ bool PDBSourceLineWriter::PrintFunctions() {
|
||||||
symbols.Release();
|
symbols.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all public symbols. Store public symbols that are not also private
|
// Find all public symbols and record public symbols that are not also private
|
||||||
// symbols for later.
|
// symbols.
|
||||||
std::set<DWORD> public_only_rvas;
|
|
||||||
hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);
|
hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);
|
||||||
|
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
|
@ -448,11 +484,8 @@ bool PDBSourceLineWriter::PrintFunctions() {
|
||||||
|
|
||||||
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
|
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
|
||||||
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
|
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
|
||||||
if (rva_symbols.find(rva) == rva_symbols.end()) {
|
// Potentially record this as the canonical symbol for this rva.
|
||||||
// Keep symbols in rva order.
|
MaybeRecordSymbol(rva, symbol, true, &rva_symbol);
|
||||||
rva_symbols.insert(std::make_pair(rva, symbol));
|
|
||||||
public_only_rvas.insert(rva);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
|
fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
|
||||||
return false;
|
return false;
|
||||||
|
@ -464,29 +497,18 @@ bool PDBSourceLineWriter::PrintFunctions() {
|
||||||
symbols.Release();
|
symbols.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each rva, dump one symbol at the address.
|
// For each rva, dump the selected symbol at the address.
|
||||||
SymbolMultimap::iterator it = rva_symbols.begin();
|
SymbolMap::iterator it;
|
||||||
while (it != rva_symbols.end()) {
|
for (it = rva_symbol.begin(); it != rva_symbol.end(); ++it) {
|
||||||
std::pair<SymbolMultimap::iterator, SymbolMultimap::iterator> symbol_range =
|
CComPtr<IDiaSymbol> symbol = it->second.symbol;
|
||||||
rva_symbols.equal_range(it->first);
|
|
||||||
// Find the minimum symbol by name to make the output more consistent
|
|
||||||
// between runs on different releases of the same module, in the case of
|
|
||||||
// multiple symbols sharing an address.
|
|
||||||
SymbolMultimap::iterator least_symbol_iter =
|
|
||||||
std::min_element(symbol_range.first, symbol_range.second, CompareSymbols);
|
|
||||||
CComPtr<IDiaSymbol> symbol = least_symbol_iter->second;
|
|
||||||
// Only print public symbols if there is no function symbol for the address.
|
// Only print public symbols if there is no function symbol for the address.
|
||||||
if (public_only_rvas.count(it->first) == 0) {
|
if (!it->second.is_public) {
|
||||||
if (!PrintFunction(symbol, symbol))
|
if (!PrintFunction(symbol, symbol, it->second.is_multiple))
|
||||||
return false;
|
return false;
|
||||||
symbol.Release();
|
|
||||||
} else {
|
} else {
|
||||||
if (!PrintCodePublicSymbol(symbol))
|
if (!PrintCodePublicSymbol(symbol, it->second.is_multiple))
|
||||||
return false;
|
return false;
|
||||||
symbol.Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it = symbol_range.second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When building with PGO, the compiler can split functions into
|
// When building with PGO, the compiler can split functions into
|
||||||
|
@ -528,7 +550,7 @@ bool PDBSourceLineWriter::PrintFunctions() {
|
||||||
SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) &&
|
SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) &&
|
||||||
SUCCEEDED(parent->get_length(&func_length))) {
|
SUCCEEDED(parent->get_length(&func_length))) {
|
||||||
if (block_rva < func_rva || block_rva > (func_rva + func_length)) {
|
if (block_rva < func_rva || block_rva > (func_rva + func_length)) {
|
||||||
if (!PrintFunction(parent, block)) {
|
if (!PrintFunction(parent, block, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,7 +867,8 @@ bool PDBSourceLineWriter::PrintFrameData() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
|
bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol,
|
||||||
|
bool has_multiple_symbols) {
|
||||||
BOOL is_code;
|
BOOL is_code;
|
||||||
if (FAILED(symbol->get_code(&is_code))) {
|
if (FAILED(symbol->get_code(&is_code))) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -868,8 +891,10 @@ bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
|
||||||
AddressRangeVector ranges;
|
AddressRangeVector ranges;
|
||||||
MapAddressRange(image_map_, AddressRange(rva, 1), &ranges);
|
MapAddressRange(image_map_, AddressRange(rva, 1), &ranges);
|
||||||
for (size_t i = 0; i < ranges.size(); ++i) {
|
for (size_t i = 0; i < ranges.size(); ++i) {
|
||||||
fprintf(output_, "PUBLIC %lx %x %ws\n", ranges[i].rva,
|
const char* optional_multiple_field =
|
||||||
stack_param_size > 0 ? stack_param_size : 0,
|
enable_multiple_field_ && has_multiple_symbols ? "m " : "";
|
||||||
|
fprintf(output_, "PUBLIC %s%lx %x %ws\n", optional_multiple_field,
|
||||||
|
ranges[i].rva, stack_param_size > 0 ? stack_param_size : 0,
|
||||||
name.m_str);
|
name.m_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,9 @@ class PDBSourceLineWriter {
|
||||||
ANY_FILE // try PDB_FILE and then EXE_FILE
|
ANY_FILE // try PDB_FILE and then EXE_FILE
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit PDBSourceLineWriter();
|
// NB: |enable_multiple_field| is temporary while transitioning to enabling
|
||||||
|
// writing the multiple field permanently.
|
||||||
|
explicit PDBSourceLineWriter(bool enable_multiple_field = false);
|
||||||
~PDBSourceLineWriter();
|
~PDBSourceLineWriter();
|
||||||
|
|
||||||
// Opens the given file. For executable files, the corresponding pdb
|
// Opens the given file. For executable files, the corresponding pdb
|
||||||
|
@ -138,11 +140,12 @@ class PDBSourceLineWriter {
|
||||||
bool PrintLines(IDiaEnumLineNumbers *lines);
|
bool PrintLines(IDiaEnumLineNumbers *lines);
|
||||||
|
|
||||||
// Outputs a function address and name, followed by its source line list.
|
// Outputs a function address and name, followed by its source line list.
|
||||||
// block can be the same object as function, or it can be a reference
|
// block can be the same object as function, or it can be a reference to a
|
||||||
// to a code block that is lexically part of this function, but
|
// code block that is lexically part of this function, but resides at a
|
||||||
// resides at a separate address.
|
// separate address. If has_multiple_symbols is true, this function's
|
||||||
// Returns true on success.
|
// instructions correspond to multiple symbols. Returns true on success.
|
||||||
bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block);
|
bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block,
|
||||||
|
bool has_multiple_symbols);
|
||||||
|
|
||||||
// Outputs all functions as described above. Returns true on success.
|
// Outputs all functions as described above. Returns true on success.
|
||||||
bool PrintFunctions();
|
bool PrintFunctions();
|
||||||
|
@ -167,8 +170,10 @@ class PDBSourceLineWriter {
|
||||||
|
|
||||||
// Outputs a single public symbol address and name, if the symbol corresponds
|
// Outputs a single public symbol address and name, if the symbol corresponds
|
||||||
// to a code address. Returns true on success. If symbol is does not
|
// to a code address. Returns true on success. If symbol is does not
|
||||||
// correspond to code, returns true without outputting anything.
|
// correspond to code, returns true without outputting anything. If
|
||||||
bool PrintCodePublicSymbol(IDiaSymbol *symbol);
|
// has_multiple_symbols is true, the symbol corresponds to a code address and
|
||||||
|
// the instructions correspond to multiple symbols.
|
||||||
|
bool PrintCodePublicSymbol(IDiaSymbol *symbol, bool has_multiple_symbols);
|
||||||
|
|
||||||
// Outputs a line identifying the PDB file that is being dumped, along with
|
// Outputs a line identifying the PDB file that is being dumped, along with
|
||||||
// its uuid and age.
|
// its uuid and age.
|
||||||
|
@ -227,6 +232,10 @@ class PDBSourceLineWriter {
|
||||||
// a failure, returns 0, which is also a valid number of bytes.
|
// a failure, returns 0, which is also a valid number of bytes.
|
||||||
static int GetFunctionStackParamSize(IDiaSymbol *function);
|
static int GetFunctionStackParamSize(IDiaSymbol *function);
|
||||||
|
|
||||||
|
// True if the optional 'm' field on FUNC and PUBLIC for multiple symbols at
|
||||||
|
// the same address should be output.
|
||||||
|
bool enable_multiple_field_;
|
||||||
|
|
||||||
// The filename of the PE file corresponding to the currently-open
|
// The filename of the PE file corresponding to the currently-open
|
||||||
// pdb file.
|
// pdb file.
|
||||||
wstring code_file_;
|
wstring code_file_;
|
||||||
|
|
|
@ -95,12 +95,14 @@ class SymbolParseHelper {
|
||||||
char **filename); // out
|
char **filename); // out
|
||||||
|
|
||||||
// Parses a |function_line| declaration. Returns true on success.
|
// Parses a |function_line| declaration. Returns true on success.
|
||||||
// Format: FUNC <address> <size> <stack_param_size> <name>.
|
// Format: FUNC [<multiple>] <address> <size> <stack_param_size> <name>.
|
||||||
// Notice, that this method modifies the input |function_line| which is why it
|
// Notice, that this method modifies the input |function_line| which is why it
|
||||||
// can't be const. On success, <address>, <size>, <stack_param_size>, and
|
// can't be const. On success, the presence of <multiple>, <address>, <size>,
|
||||||
// <name> are stored in |*address|, |*size|, |*stack_param_size|, and |*name|.
|
// <stack_param_size>, and <name> are stored in |*is_multiple|, |*address|,
|
||||||
// No allocation is done, |*name| simply points inside |function_line|.
|
// |*size|, |*stack_param_size|, and |*name|. No allocation is done, |*name|
|
||||||
|
// simply points inside |function_line|.
|
||||||
static bool ParseFunction(char *function_line, // in
|
static bool ParseFunction(char *function_line, // in
|
||||||
|
bool *is_multiple, // out
|
||||||
uint64_t *address, // out
|
uint64_t *address, // out
|
||||||
uint64_t *size, // out
|
uint64_t *size, // out
|
||||||
long *stack_param_size, // out
|
long *stack_param_size, // out
|
||||||
|
@ -119,12 +121,14 @@ class SymbolParseHelper {
|
||||||
long *source_file); // out
|
long *source_file); // out
|
||||||
|
|
||||||
// Parses a |public_line| declaration. Returns true on success.
|
// Parses a |public_line| declaration. Returns true on success.
|
||||||
// Format: PUBLIC <address> <stack_param_size> <name>
|
// Format: PUBLIC [<multiple>] <address> <stack_param_size> <name>
|
||||||
// Notice, that this method modifies the input |function_line| which is why
|
// Notice, that this method modifies the input |function_line| which is why
|
||||||
// it can't be const. On success, <address>, <stack_param_size>, <name>
|
// it can't be const. On success, the presence of <multiple>, <address>,
|
||||||
// are stored in |*address|, |*stack_param_size|, and |*name|.
|
// <stack_param_size>, <name> are stored in |*is_multiple|, |*address|,
|
||||||
// No allocation is done, |*name| simply points inside |public_line|.
|
// |*stack_param_size|, and |*name|. No allocation is done, |*name| simply
|
||||||
|
// points inside |public_line|.
|
||||||
static bool ParsePublicSymbol(char *public_line, // in
|
static bool ParsePublicSymbol(char *public_line, // in
|
||||||
|
bool *is_multiple, // out
|
||||||
uint64_t *address, // out
|
uint64_t *address, // out
|
||||||
long *stack_param_size, // out
|
long *stack_param_size, // out
|
||||||
char **name); // out
|
char **name); // out
|
||||||
|
|
|
@ -62,6 +62,42 @@ namespace google_breakpad {
|
||||||
#define strtoull _strtoui64
|
#define strtoull _strtoui64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Utility function to tokenize given the presence of an optional initial
|
||||||
|
// field. In this case, optional_field is the expected string for the optional
|
||||||
|
// field, and max_tokens is the maximum number of tokens including the optional
|
||||||
|
// field. Refer to the documentation for Tokenize for descriptions of the other
|
||||||
|
// arguments.
|
||||||
|
bool TokenizeWithOptionalField(char *line,
|
||||||
|
const char *optional_field,
|
||||||
|
const char *separators,
|
||||||
|
int max_tokens,
|
||||||
|
vector<char*> *tokens) {
|
||||||
|
// First tokenize assuming the optional field is not present. If we then see
|
||||||
|
// the optional field, additionally tokenize the last token into two tokens.
|
||||||
|
if (!Tokenize(line, separators, max_tokens - 1, tokens)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(tokens->front(), optional_field) == 0) {
|
||||||
|
// The optional field is present. Split the last token in two to recover the
|
||||||
|
// field prior to the last.
|
||||||
|
vector<char*> last_tokens;
|
||||||
|
if (!Tokenize(tokens->back(), separators, 2, &last_tokens)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Replace the previous last token with the two new tokens.
|
||||||
|
tokens->pop_back();
|
||||||
|
tokens->push_back(last_tokens[0]);
|
||||||
|
tokens->push_back(last_tokens[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
static const char *kWhitespace = " \r\n";
|
static const char *kWhitespace = " \r\n";
|
||||||
static const int kMaxErrorsPrinted = 5;
|
static const int kMaxErrorsPrinted = 5;
|
||||||
static const int kMaxErrorsBeforeBailing = 100;
|
static const int kMaxErrorsBeforeBailing = 100;
|
||||||
|
@ -323,13 +359,14 @@ bool BasicSourceLineResolver::Module::ParseFile(char *file_line) {
|
||||||
|
|
||||||
BasicSourceLineResolver::Function*
|
BasicSourceLineResolver::Function*
|
||||||
BasicSourceLineResolver::Module::ParseFunction(char *function_line) {
|
BasicSourceLineResolver::Module::ParseFunction(char *function_line) {
|
||||||
|
bool is_multiple;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
long stack_param_size;
|
long stack_param_size;
|
||||||
char *name;
|
char *name;
|
||||||
if (SymbolParseHelper::ParseFunction(function_line, &address, &size,
|
if (SymbolParseHelper::ParseFunction(function_line, &is_multiple, &address,
|
||||||
&stack_param_size, &name)) {
|
&size, &stack_param_size, &name)) {
|
||||||
return new Function(name, address, size, stack_param_size);
|
return new Function(name, address, size, stack_param_size, is_multiple);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -349,11 +386,12 @@ BasicSourceLineResolver::Line* BasicSourceLineResolver::Module::ParseLine(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
|
bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
|
||||||
|
bool is_multiple;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
long stack_param_size;
|
long stack_param_size;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
if (SymbolParseHelper::ParsePublicSymbol(public_line, &address,
|
if (SymbolParseHelper::ParsePublicSymbol(public_line, &is_multiple, &address,
|
||||||
&stack_param_size, &name)) {
|
&stack_param_size, &name)) {
|
||||||
// A few public symbols show up with an address of 0. This has been seen
|
// A few public symbols show up with an address of 0. This has been seen
|
||||||
// in the dumped output of ntdll.pdb for symbols such as _CIlog, _CIpow,
|
// in the dumped output of ntdll.pdb for symbols such as _CIlog, _CIpow,
|
||||||
|
@ -366,7 +404,8 @@ bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
linked_ptr<PublicSymbol> symbol(new PublicSymbol(name, address,
|
linked_ptr<PublicSymbol> symbol(new PublicSymbol(name, address,
|
||||||
stack_param_size));
|
stack_param_size,
|
||||||
|
is_multiple));
|
||||||
return public_symbols_.Store(address, symbol);
|
return public_symbols_.Store(address, symbol);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -491,36 +530,39 @@ bool SymbolParseHelper::ParseFile(char *file_line, long *index,
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool SymbolParseHelper::ParseFunction(char *function_line, uint64_t *address,
|
bool SymbolParseHelper::ParseFunction(char *function_line, bool *is_multiple,
|
||||||
uint64_t *size, long *stack_param_size,
|
uint64_t *address, uint64_t *size,
|
||||||
char **name) {
|
long *stack_param_size, char **name) {
|
||||||
// FUNC <address> <size> <stack_param_size> <name>
|
// FUNC [<multiple>] <address> <size> <stack_param_size> <name>
|
||||||
assert(strncmp(function_line, "FUNC ", 5) == 0);
|
assert(strncmp(function_line, "FUNC ", 5) == 0);
|
||||||
function_line += 5; // skip prefix
|
function_line += 5; // skip prefix
|
||||||
|
|
||||||
vector<char*> tokens;
|
vector<char*> tokens;
|
||||||
if (!Tokenize(function_line, kWhitespace, 4, &tokens)) {
|
if (!TokenizeWithOptionalField(function_line, "m", kWhitespace, 5, &tokens)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*is_multiple = strcmp(tokens[0], "m") == 0;
|
||||||
|
int next_token = *is_multiple ? 1 : 0;
|
||||||
|
|
||||||
char *after_number;
|
char *after_number;
|
||||||
*address = strtoull(tokens[0], &after_number, 16);
|
*address = strtoull(tokens[next_token++], &after_number, 16);
|
||||||
if (!IsValidAfterNumber(after_number) ||
|
if (!IsValidAfterNumber(after_number) ||
|
||||||
*address == std::numeric_limits<unsigned long long>::max()) {
|
*address == std::numeric_limits<unsigned long long>::max()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*size = strtoull(tokens[1], &after_number, 16);
|
*size = strtoull(tokens[next_token++], &after_number, 16);
|
||||||
if (!IsValidAfterNumber(after_number) ||
|
if (!IsValidAfterNumber(after_number) ||
|
||||||
*size == std::numeric_limits<unsigned long long>::max()) {
|
*size == std::numeric_limits<unsigned long long>::max()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*stack_param_size = strtol(tokens[2], &after_number, 16);
|
*stack_param_size = strtol(tokens[next_token++], &after_number, 16);
|
||||||
if (!IsValidAfterNumber(after_number) ||
|
if (!IsValidAfterNumber(after_number) ||
|
||||||
*stack_param_size == std::numeric_limits<long>::max() ||
|
*stack_param_size == std::numeric_limits<long>::max() ||
|
||||||
*stack_param_size < 0) {
|
*stack_param_size < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*name = tokens[3];
|
*name = tokens[next_token++];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -571,32 +613,35 @@ bool SymbolParseHelper::ParseLine(char *line_line, uint64_t *address,
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool SymbolParseHelper::ParsePublicSymbol(char *public_line,
|
bool SymbolParseHelper::ParsePublicSymbol(char *public_line, bool *is_multiple,
|
||||||
uint64_t *address,
|
uint64_t *address,
|
||||||
long *stack_param_size,
|
long *stack_param_size,
|
||||||
char **name) {
|
char **name) {
|
||||||
// PUBLIC <address> <stack_param_size> <name>
|
// PUBLIC [<multiple>] <address> <stack_param_size> <name>
|
||||||
assert(strncmp(public_line, "PUBLIC ", 7) == 0);
|
assert(strncmp(public_line, "PUBLIC ", 7) == 0);
|
||||||
public_line += 7; // skip prefix
|
public_line += 7; // skip prefix
|
||||||
|
|
||||||
vector<char*> tokens;
|
vector<char*> tokens;
|
||||||
if (!Tokenize(public_line, kWhitespace, 3, &tokens)) {
|
if (!TokenizeWithOptionalField(public_line, "m", kWhitespace, 4, &tokens)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*is_multiple = strcmp(tokens[0], "m") == 0;
|
||||||
|
int next_token = *is_multiple ? 1 : 0;
|
||||||
|
|
||||||
char *after_number;
|
char *after_number;
|
||||||
*address = strtoull(tokens[0], &after_number, 16);
|
*address = strtoull(tokens[next_token++], &after_number, 16);
|
||||||
if (!IsValidAfterNumber(after_number) ||
|
if (!IsValidAfterNumber(after_number) ||
|
||||||
*address == std::numeric_limits<unsigned long long>::max()) {
|
*address == std::numeric_limits<unsigned long long>::max()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*stack_param_size = strtol(tokens[1], &after_number, 16);
|
*stack_param_size = strtol(tokens[next_token++], &after_number, 16);
|
||||||
if (!IsValidAfterNumber(after_number) ||
|
if (!IsValidAfterNumber(after_number) ||
|
||||||
*stack_param_size == std::numeric_limits<long>::max() ||
|
*stack_param_size == std::numeric_limits<long>::max() ||
|
||||||
*stack_param_size < 0) {
|
*stack_param_size < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*name = tokens[2];
|
*name = tokens[next_token++];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,11 +60,13 @@ BasicSourceLineResolver::Function : public SourceLineResolverBase::Function {
|
||||||
Function(const string &function_name,
|
Function(const string &function_name,
|
||||||
MemAddr function_address,
|
MemAddr function_address,
|
||||||
MemAddr code_size,
|
MemAddr code_size,
|
||||||
int set_parameter_size) : Base(function_name,
|
int set_parameter_size,
|
||||||
function_address,
|
bool is_mutiple) : Base(function_name,
|
||||||
code_size,
|
function_address,
|
||||||
set_parameter_size),
|
code_size,
|
||||||
lines() { }
|
set_parameter_size,
|
||||||
|
is_mutiple),
|
||||||
|
lines() { }
|
||||||
RangeMap< MemAddr, linked_ptr<Line> > lines;
|
RangeMap< MemAddr, linked_ptr<Line> > lines;
|
||||||
private:
|
private:
|
||||||
typedef SourceLineResolverBase::Function Base;
|
typedef SourceLineResolverBase::Function Base;
|
||||||
|
|
|
@ -455,16 +455,19 @@ TEST(SymbolParseHelper, ParseFileInvalid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test parsing of valid FUNC lines. The format is:
|
// Test parsing of valid FUNC lines. The format is:
|
||||||
// FUNC <address> <size> <stack_param_size> <name>
|
// FUNC [<multiple>] <address> <size> <stack_param_size> <name>
|
||||||
TEST(SymbolParseHelper, ParseFunctionValid) {
|
TEST(SymbolParseHelper, ParseFunctionValid) {
|
||||||
|
bool multiple;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
long stack_param_size;
|
long stack_param_size;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
char kTestLine[] = "FUNC 1 2 3 function name";
|
char kTestLine[] = "FUNC 1 2 3 function name";
|
||||||
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine, &address, &size,
|
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_FALSE(multiple);
|
||||||
EXPECT_EQ(1ULL, address);
|
EXPECT_EQ(1ULL, address);
|
||||||
EXPECT_EQ(2ULL, size);
|
EXPECT_EQ(2ULL, size);
|
||||||
EXPECT_EQ(3, stack_param_size);
|
EXPECT_EQ(3, stack_param_size);
|
||||||
|
@ -472,25 +475,41 @@ TEST(SymbolParseHelper, ParseFunctionValid) {
|
||||||
|
|
||||||
// Test hex address, size, and param size.
|
// Test hex address, size, and param size.
|
||||||
char kTestLine1[] = "FUNC a1 a2 a3 function name";
|
char kTestLine1[] = "FUNC a1 a2 a3 function name";
|
||||||
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine1, &address, &size,
|
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine1, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_FALSE(multiple);
|
||||||
EXPECT_EQ(0xa1ULL, address);
|
EXPECT_EQ(0xa1ULL, address);
|
||||||
EXPECT_EQ(0xa2ULL, size);
|
EXPECT_EQ(0xa2ULL, size);
|
||||||
EXPECT_EQ(0xa3, stack_param_size);
|
EXPECT_EQ(0xa3, stack_param_size);
|
||||||
EXPECT_EQ("function name", string(name));
|
EXPECT_EQ("function name", string(name));
|
||||||
|
|
||||||
char kTestLine2[] = "FUNC 0 0 0 function name";
|
char kTestLine2[] = "FUNC 0 0 0 function name";
|
||||||
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine2, &address, &size,
|
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine2, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_FALSE(multiple);
|
||||||
EXPECT_EQ(0ULL, address);
|
EXPECT_EQ(0ULL, address);
|
||||||
EXPECT_EQ(0ULL, size);
|
EXPECT_EQ(0ULL, size);
|
||||||
EXPECT_EQ(0, stack_param_size);
|
EXPECT_EQ(0, stack_param_size);
|
||||||
EXPECT_EQ("function name", string(name));
|
EXPECT_EQ("function name", string(name));
|
||||||
|
|
||||||
|
// Test optional multiple field.
|
||||||
|
char kTestLine3[] = "FUNC m a1 a2 a3 function name";
|
||||||
|
ASSERT_TRUE(SymbolParseHelper::ParseFunction(kTestLine3, &multiple, &address,
|
||||||
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_TRUE(multiple);
|
||||||
|
EXPECT_EQ(0xa1ULL, address);
|
||||||
|
EXPECT_EQ(0xa2ULL, size);
|
||||||
|
EXPECT_EQ(0xa3, stack_param_size);
|
||||||
|
EXPECT_EQ("function name", string(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test parsing of invalid FUNC lines. The format is:
|
// Test parsing of invalid FUNC lines. The format is:
|
||||||
// FUNC <address> <size> <stack_param_size> <name>
|
// FUNC [<multiple>] <address> <size> <stack_param_size> <name>
|
||||||
TEST(SymbolParseHelper, ParseFunctionInvalid) {
|
TEST(SymbolParseHelper, ParseFunctionInvalid) {
|
||||||
|
bool multiple;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
long stack_param_size;
|
long stack_param_size;
|
||||||
|
@ -498,36 +517,49 @@ TEST(SymbolParseHelper, ParseFunctionInvalid) {
|
||||||
|
|
||||||
// Test missing function name.
|
// Test missing function name.
|
||||||
char kTestLine[] = "FUNC 1 2 3 ";
|
char kTestLine[] = "FUNC 1 2 3 ";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test bad address.
|
// Test bad address.
|
||||||
char kTestLine1[] = "FUNC 1z 2 3 function name";
|
char kTestLine1[] = "FUNC 1z 2 3 function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine1, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine1, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test large address.
|
// Test large address.
|
||||||
char kTestLine2[] = "FUNC 123123123123123123123123123 2 3 function name";
|
char kTestLine2[] = "FUNC 123123123123123123123123123 2 3 function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine2, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine2, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test bad size.
|
// Test bad size.
|
||||||
char kTestLine3[] = "FUNC 1 z2 3 function name";
|
char kTestLine3[] = "FUNC 1 z2 3 function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine3, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine3, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test large size.
|
// Test large size.
|
||||||
char kTestLine4[] = "FUNC 1 231231231231231231231231232 3 function name";
|
char kTestLine4[] = "FUNC 1 231231231231231231231231232 3 function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine4, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine4, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test bad param size.
|
// Test bad param size.
|
||||||
char kTestLine5[] = "FUNC 1 2 3z function name";
|
char kTestLine5[] = "FUNC 1 2 3z function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine5, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine5, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test large param size.
|
// Test large param size.
|
||||||
char kTestLine6[] = "FUNC 1 2 312312312312312312312312323 function name";
|
char kTestLine6[] = "FUNC 1 2 312312312312312312312312323 function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine6, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine6, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Negative param size.
|
// Negative param size.
|
||||||
char kTestLine7[] = "FUNC 1 2 -5 function name";
|
char kTestLine7[] = "FUNC 1 2 -5 function name";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine7, &address, &size,
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine7, &multiple, &address,
|
||||||
&stack_param_size, &name));
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
// Test invalid optional field.
|
||||||
|
char kTestLine8[] = "FUNC x 1 2 5 function name";
|
||||||
|
ASSERT_FALSE(SymbolParseHelper::ParseFunction(kTestLine8, &multiple, &address,
|
||||||
|
&size, &stack_param_size,
|
||||||
|
&name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test parsing of valid lines. The format is:
|
// Test parsing of valid lines. The format is:
|
||||||
|
@ -612,67 +644,96 @@ TEST(SymbolParseHelper, ParseLineInvalid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test parsing of valid PUBLIC lines. The format is:
|
// Test parsing of valid PUBLIC lines. The format is:
|
||||||
// PUBLIC <address> <stack_param_size> <name>
|
// PUBLIC [<multiple>] <address> <stack_param_size> <name>
|
||||||
TEST(SymbolParseHelper, ParsePublicSymbolValid) {
|
TEST(SymbolParseHelper, ParsePublicSymbolValid) {
|
||||||
|
bool multiple;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
long stack_param_size;
|
long stack_param_size;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
char kTestLine[] = "PUBLIC 1 2 3";
|
char kTestLine[] = "PUBLIC 1 2 3";
|
||||||
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine, &address,
|
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_FALSE(multiple);
|
||||||
EXPECT_EQ(1ULL, address);
|
EXPECT_EQ(1ULL, address);
|
||||||
EXPECT_EQ(2, stack_param_size);
|
EXPECT_EQ(2, stack_param_size);
|
||||||
EXPECT_EQ("3", string(name));
|
EXPECT_EQ("3", string(name));
|
||||||
|
|
||||||
// Test hex size and address.
|
// Test hex size and address.
|
||||||
char kTestLine1[] = "PUBLIC a1 a2 function name";
|
char kTestLine1[] = "PUBLIC a1 a2 function name";
|
||||||
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine1, &address,
|
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine1, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_FALSE(multiple);
|
||||||
EXPECT_EQ(0xa1ULL, address);
|
EXPECT_EQ(0xa1ULL, address);
|
||||||
EXPECT_EQ(0xa2, stack_param_size);
|
EXPECT_EQ(0xa2, stack_param_size);
|
||||||
EXPECT_EQ("function name", string(name));
|
EXPECT_EQ("function name", string(name));
|
||||||
|
|
||||||
// Test 0 is a valid address.
|
// Test 0 is a valid address.
|
||||||
char kTestLine2[] = "PUBLIC 0 a2 function name";
|
char kTestLine2[] = "PUBLIC 0 a2 function name";
|
||||||
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine2, &address,
|
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine2, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_FALSE(multiple);
|
||||||
EXPECT_EQ(0ULL, address);
|
EXPECT_EQ(0ULL, address);
|
||||||
EXPECT_EQ(0xa2, stack_param_size);
|
EXPECT_EQ(0xa2, stack_param_size);
|
||||||
EXPECT_EQ("function name", string(name));
|
EXPECT_EQ("function name", string(name));
|
||||||
|
|
||||||
|
// Test optional multiple field.
|
||||||
|
char kTestLine3[] = "PUBLIC m a1 a2 function name";
|
||||||
|
ASSERT_TRUE(SymbolParseHelper::ParsePublicSymbol(kTestLine3, &multiple,
|
||||||
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
EXPECT_TRUE(multiple);
|
||||||
|
EXPECT_EQ(0xa1ULL, address);
|
||||||
|
EXPECT_EQ(0xa2, stack_param_size);
|
||||||
|
EXPECT_EQ("function name", string(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test parsing of invalid PUBLIC lines. The format is:
|
// Test parsing of invalid PUBLIC lines. The format is:
|
||||||
// PUBLIC <address> <stack_param_size> <name>
|
// PUBLIC [<multiple>] <address> <stack_param_size> <name>
|
||||||
TEST(SymbolParseHelper, ParsePublicSymbolInvalid) {
|
TEST(SymbolParseHelper, ParsePublicSymbolInvalid) {
|
||||||
|
bool multiple;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
long stack_param_size;
|
long stack_param_size;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
// Test missing source function name.
|
// Test missing source function name.
|
||||||
char kTestLine[] = "PUBLIC 1 2 ";
|
char kTestLine[] = "PUBLIC 1 2 ";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine, &address,
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test bad address.
|
// Test bad address.
|
||||||
char kTestLine1[] = "PUBLIC 1z 2 3";
|
char kTestLine1[] = "PUBLIC 1z 2 3";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine1, &address,
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine1, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test large address.
|
// Test large address.
|
||||||
char kTestLine2[] = "PUBLIC 123123123123123123123123 2 3";
|
char kTestLine2[] = "PUBLIC 123123123123123123123123 2 3";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine2, &address,
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine2, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test bad param stack size.
|
// Test bad param stack size.
|
||||||
char kTestLine3[] = "PUBLIC 1 z2 3";
|
char kTestLine3[] = "PUBLIC 1 z2 3";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine3, &address,
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine3, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test large param stack size.
|
// Test large param stack size.
|
||||||
char kTestLine4[] = "PUBLIC 1 123123123123123123123123123 3";
|
char kTestLine4[] = "PUBLIC 1 123123123123123123123123123 3";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine4, &address,
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine4, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
// Test negative param stack size.
|
// Test negative param stack size.
|
||||||
char kTestLine5[] = "PUBLIC 1 -5 3";
|
char kTestLine5[] = "PUBLIC 1 -5 3";
|
||||||
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine5, &address,
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine5, &multiple,
|
||||||
&stack_param_size, &name));
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
|
// Test invalid optional field.
|
||||||
|
char kTestLine6[] = "PUBLIC x 1 5 3";
|
||||||
|
ASSERT_FALSE(SymbolParseHelper::ParsePublicSymbol(kTestLine6, &multiple,
|
||||||
|
&address, &stack_param_size,
|
||||||
|
&name));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -85,9 +85,10 @@ struct SourceLineResolverBase::Function {
|
||||||
Function(const string &function_name,
|
Function(const string &function_name,
|
||||||
MemAddr function_address,
|
MemAddr function_address,
|
||||||
MemAddr code_size,
|
MemAddr code_size,
|
||||||
int set_parameter_size)
|
int set_parameter_size,
|
||||||
|
bool is_multiple)
|
||||||
: name(function_name), address(function_address), size(code_size),
|
: name(function_name), address(function_address), size(code_size),
|
||||||
parameter_size(set_parameter_size) { }
|
parameter_size(set_parameter_size), is_multiple(is_multiple) { }
|
||||||
|
|
||||||
string name;
|
string name;
|
||||||
MemAddr address;
|
MemAddr address;
|
||||||
|
@ -95,16 +96,21 @@ struct SourceLineResolverBase::Function {
|
||||||
|
|
||||||
// The size of parameters passed to this function on the stack.
|
// The size of parameters passed to this function on the stack.
|
||||||
int32_t parameter_size;
|
int32_t parameter_size;
|
||||||
|
|
||||||
|
// If the function's instructions correspond to multiple symbols.
|
||||||
|
bool is_multiple;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SourceLineResolverBase::PublicSymbol {
|
struct SourceLineResolverBase::PublicSymbol {
|
||||||
PublicSymbol() { }
|
PublicSymbol() { }
|
||||||
PublicSymbol(const string& set_name,
|
PublicSymbol(const string& set_name,
|
||||||
MemAddr set_address,
|
MemAddr set_address,
|
||||||
int set_parameter_size)
|
int set_parameter_size,
|
||||||
|
bool is_multiple)
|
||||||
: name(set_name),
|
: name(set_name),
|
||||||
address(set_address),
|
address(set_address),
|
||||||
parameter_size(set_parameter_size) {}
|
parameter_size(set_parameter_size),
|
||||||
|
is_multiple(is_multiple) {}
|
||||||
|
|
||||||
string name;
|
string name;
|
||||||
MemAddr address;
|
MemAddr address;
|
||||||
|
@ -113,6 +119,9 @@ struct SourceLineResolverBase::PublicSymbol {
|
||||||
// is set to the size of the parameters passed to the funciton on the
|
// is set to the size of the parameters passed to the funciton on the
|
||||||
// stack, if known.
|
// stack, if known.
|
||||||
int32_t parameter_size;
|
int32_t parameter_size;
|
||||||
|
|
||||||
|
// If the function's instructions correspond to multiple symbols.
|
||||||
|
bool is_multiple;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SourceLineResolverBase::Module {
|
class SourceLineResolverBase::Module {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -31,6 +31,7 @@
|
||||||
// a text-based format that we can use from the minidump processor.
|
// a text-based format that we can use from the minidump processor.
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -41,12 +42,22 @@ using google_breakpad::PDBSourceLineWriter;
|
||||||
|
|
||||||
int wmain(int argc, wchar_t **argv) {
|
int wmain(int argc, wchar_t **argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
fprintf(stderr, "Usage: %ws <file.[pdb|exe|dll]>\n", argv[0]);
|
fprintf(stderr,
|
||||||
|
"Usage: %ws [--enable_multiple_field] <file.[pdb|exe|dll]>\n",
|
||||||
|
argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PDBSourceLineWriter writer;
|
// This is a temporary option to enable writing the optional 'm' field on FUNC
|
||||||
if (!writer.Open(wstring(argv[1]), PDBSourceLineWriter::ANY_FILE)) {
|
// and PUBLIC, denoting multiple symbols for the address. This option will be
|
||||||
|
// removed, with the behavior enabled by default, after all symbol file
|
||||||
|
// readers have had a chance to update.
|
||||||
|
bool enable_multiple_field =
|
||||||
|
(argc >= 3 && wcscmp(L"--enable_multiple_field", argv[1]) == 0);
|
||||||
|
|
||||||
|
PDBSourceLineWriter writer(enable_multiple_field);
|
||||||
|
if (!writer.Open(wstring(argv[enable_multiple_field ? 2 : 1]),
|
||||||
|
PDBSourceLineWriter::ANY_FILE)) {
|
||||||
fprintf(stderr, "Open failed\n");
|
fprintf(stderr, "Open failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1262,7 +1262,7 @@ FUNC 3320 18 0 NLG_Notify
|
||||||
332a 5 107 69
|
332a 5 107 69
|
||||||
332f 7 108 69
|
332f 7 108 69
|
||||||
3336 a 109 69
|
3336 a 109 69
|
||||||
FUNC 3340 1 0 _NLG_Dispatch
|
FUNC 3340 1 0 _NLG_Dispatch2
|
||||||
3340 0 113 69
|
3340 0 113 69
|
||||||
3340 10 114 69
|
3340 10 114 69
|
||||||
FUNC 3350 1 0 _NLG_Return2
|
FUNC 3350 1 0 _NLG_Return2
|
||||||
|
|
Loading…
Reference in a new issue