in src/common/windows/pdb_source_line_writer.cc [441:566]
bool PDBSourceLineWriter::PrintFunctions() {
ULONG count = 0;
DWORD rva = 0;
CComPtr<IDiaSymbol> global;
HRESULT hr;
if (FAILED(session_->get_globalScope(&global))) {
fprintf(stderr, "get_globalScope failed\n");
return false;
}
CComPtr<IDiaEnumSymbols> symbols = NULL;
// Find all function symbols first.
SymbolMap rva_symbol;
hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);
if (SUCCEEDED(hr)) {
CComPtr<IDiaSymbol> symbol = NULL;
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
// 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;
}
symbol.Release();
}
symbols.Release();
}
// Find all public symbols and record public symbols that are not also private
// symbols.
hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);
if (SUCCEEDED(hr)) {
CComPtr<IDiaSymbol> symbol = NULL;
while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
if (SUCCEEDED(symbol->get_relativeVirtualAddress(&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;
}
symbol.Release();
}
symbols.Release();
}
// 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 (!it->second.is_public) {
if (!PrintFunction(symbol, symbol, it->second.is_multiple))
return false;
} else {
if (!PrintCodePublicSymbol(symbol, it->second.is_multiple))
return false;
}
}
// 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<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, false)) {
return false;
}
}
}
}
parent.Release();
block.Release();
}
blocks.Release();
compiland.Release();
}
global.Release();
return true;
}