aboutsummaryrefslogtreecommitdiff
path: root/src/common/windows
diff options
context:
space:
mode:
authorMike Wittman <wittman@chromium.org>2017-11-29 13:29:37 -0800
committerMark Mentovai <mark@chromium.org>2017-11-29 21:33:23 +0000
commitb1226959a25b6a5311801d6f204b088c706e7c25 (patch)
tree3dfc65dab8bb0dcd929748873de179ce259030f5 /src/common/windows
parentUpdate test data for identical-code-folded symbol changes (diff)
downloadbreakpad-b1226959a25b6a5311801d6f204b088c706e7c25.tar.xz
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>
Diffstat (limited to 'src/common/windows')
-rw-r--r--src/common/windows/pdb_source_line_writer.cc127
-rw-r--r--src/common/windows/pdb_source_line_writer.h25
2 files changed, 93 insertions, 59 deletions
diff --git a/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc
index dace8860..19c63852 100644
--- a/src/common/windows/pdb_source_line_writer.cc
+++ b/src/common/windows/pdb_source_line_writer.cc
@@ -109,7 +109,53 @@ namespace {
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(&current_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.
class AutoImage {
@@ -171,19 +217,10 @@ bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) {
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
-PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
+PDBSourceLineWriter::PDBSourceLineWriter(bool enable_multiple_field)
+ : enable_multiple_field_(enable_multiple_field), output_(NULL) {
}
PDBSourceLineWriter::~PDBSourceLineWriter() {
@@ -299,7 +336,8 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
}
bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
- IDiaSymbol *block) {
+ IDiaSymbol *block,
+ bool has_multiple_symbols) {
// The function format is:
// FUNC <address> <length> <param_stack_size> <function>
DWORD rva;
@@ -335,9 +373,10 @@ bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)),
&ranges);
for (size_t i = 0; i < ranges.size(); ++i) {
- fprintf(output_, "FUNC %lx %lx %x %ws\n",
- ranges[i].rva, ranges[i].length, stack_param_size,
- name.m_str);
+ const char* optional_multiple_field =
+ enable_multiple_field_ && has_multiple_symbols ? "m " : "";
+ 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;
@@ -415,7 +454,7 @@ bool PDBSourceLineWriter::PrintFunctions() {
CComPtr<IDiaEnumSymbols> symbols = NULL;
// Find all function symbols first.
- SymbolMultimap rva_symbols;
+ SymbolMap rva_symbol;
hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);
if (SUCCEEDED(hr)) {
@@ -423,10 +462,8 @@ bool PDBSourceLineWriter::PrintFunctions() {
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
- // Place the symbols into a multimap indexed by rva, so we can choose
- // the apropriate symbol name to use when multiple symbols share an
- // address.
- rva_symbols.insert(std::make_pair(rva, symbol));
+ // Potentially record this as the canonical symbol for this rva.
+ MaybeRecordSymbol(rva, symbol, false, &rva_symbol);
} else {
fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
return false;
@@ -438,9 +475,8 @@ bool PDBSourceLineWriter::PrintFunctions() {
symbols.Release();
}
- // Find all public symbols. Store public symbols that are not also private
- // symbols for later.
- std::set<DWORD> public_only_rvas;
+ // Find all public symbols and record public symbols that are not also private
+ // symbols.
hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);
if (SUCCEEDED(hr)) {
@@ -448,11 +484,8 @@ bool PDBSourceLineWriter::PrintFunctions() {
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
- if (rva_symbols.find(rva) == rva_symbols.end()) {
- // Keep symbols in rva order.
- rva_symbols.insert(std::make_pair(rva, symbol));
- public_only_rvas.insert(rva);
- }
+ // Potentially record this as the canonical symbol for this rva.
+ MaybeRecordSymbol(rva, symbol, true, &rva_symbol);
} else {
fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
return false;
@@ -464,29 +497,18 @@ bool PDBSourceLineWriter::PrintFunctions() {
symbols.Release();
}
- // For each rva, dump one symbol at the address.
- SymbolMultimap::iterator it = rva_symbols.begin();
- while (it != rva_symbols.end()) {
- std::pair<SymbolMultimap::iterator, SymbolMultimap::iterator> symbol_range =
- 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;
+ // For each rva, dump the selected symbol at the address.
+ SymbolMap::iterator it;
+ for (it = rva_symbol.begin(); it != rva_symbol.end(); ++it) {
+ CComPtr<IDiaSymbol> symbol = it->second.symbol;
// Only print public symbols if there is no function symbol for the address.
- if (public_only_rvas.count(it->first) == 0) {
- if (!PrintFunction(symbol, symbol))
+ if (!it->second.is_public) {
+ if (!PrintFunction(symbol, symbol, it->second.is_multiple))
return false;
- symbol.Release();
} else {
- if (!PrintCodePublicSymbol(symbol))
+ if (!PrintCodePublicSymbol(symbol, it->second.is_multiple))
return false;
- symbol.Release();
}
-
- it = symbol_range.second;
}
// 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_length(&func_length))) {
if (block_rva < func_rva || block_rva > (func_rva + func_length)) {
- if (!PrintFunction(parent, block)) {
+ if (!PrintFunction(parent, block, false)) {
return false;
}
}
@@ -845,7 +867,8 @@ bool PDBSourceLineWriter::PrintFrameData() {
return false;
}
-bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
+bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol,
+ bool has_multiple_symbols) {
BOOL is_code;
if (FAILED(symbol->get_code(&is_code))) {
return false;
@@ -868,8 +891,10 @@ bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
AddressRangeVector ranges;
MapAddressRange(image_map_, AddressRange(rva, 1), &ranges);
for (size_t i = 0; i < ranges.size(); ++i) {
- fprintf(output_, "PUBLIC %lx %x %ws\n", ranges[i].rva,
- stack_param_size > 0 ? stack_param_size : 0,
+ const char* optional_multiple_field =
+ 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);
}
diff --git a/src/common/windows/pdb_source_line_writer.h b/src/common/windows/pdb_source_line_writer.h
index e9e89bb2..5a8bcbe7 100644
--- a/src/common/windows/pdb_source_line_writer.h
+++ b/src/common/windows/pdb_source_line_writer.h
@@ -92,7 +92,9 @@ class PDBSourceLineWriter {
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();
// Opens the given file. For executable files, the corresponding pdb
@@ -138,11 +140,12 @@ class PDBSourceLineWriter {
bool PrintLines(IDiaEnumLineNumbers *lines);
// 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
- // to a code block that is lexically part of this function, but
- // resides at a separate address.
- // Returns true on success.
- bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block);
+ // block can be the same object as function, or it can be a reference to a
+ // code block that is lexically part of this function, but resides at a
+ // separate address. If has_multiple_symbols is true, this function's
+ // instructions correspond to multiple symbols. Returns true on success.
+ bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block,
+ bool has_multiple_symbols);
// Outputs all functions as described above. Returns true on success.
bool PrintFunctions();
@@ -167,8 +170,10 @@ class PDBSourceLineWriter {
// 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
- // correspond to code, returns true without outputting anything.
- bool PrintCodePublicSymbol(IDiaSymbol *symbol);
+ // correspond to code, returns true without outputting anything. If
+ // 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
// its uuid and age.
@@ -227,6 +232,10 @@ class PDBSourceLineWriter {
// a failure, returns 0, which is also a valid number of bytes.
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
// pdb file.
wstring code_file_;