in src/common/windows/pdb_source_line_writer.cc [570:723]
bool PDBSourceLineWriter::PrintFrameDataUsingPDB() {
// 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<IDiaEnumFrameData> frame_data_enum;
if (!FindTable(session_, &frame_data_enum))
return false;
DWORD last_type = std::numeric_limits<DWORD>::max();
DWORD last_rva = std::numeric_limits<DWORD>::max();
DWORD last_code_size = 0;
DWORD last_prolog_size = std::numeric_limits<DWORD>::max();
CComPtr<IDiaFrameData> frame_data;
ULONG count = 0;
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;
// 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) {
// The prolog and the code portions of the frame have to be treated
// independently as they may have independently changed in size, or may
// even have been split.
// NOTE: If epilog size is ever non-zero, we have to do something
// similar with it.
// Figure out where the prolog bytes have landed.
AddressRangeVector prolog_ranges;
if (prolog_size > 0) {
MapAddressRange(image_map_, AddressRange(rva, prolog_size),
&prolog_ranges);
}
// And figure out where the code bytes have landed.
AddressRangeVector code_ranges;
MapAddressRange(image_map_,
AddressRange(rva + prolog_size,
code_size - prolog_size),
&code_ranges);
struct FrameInfo {
DWORD rva;
DWORD code_size;
DWORD prolog_size;
};
std::vector<FrameInfo> frame_infos;
// Special case: The prolog and the code bytes remain contiguous. This is
// only done for compactness of the symbol file, and we could actually
// be outputting independent frame info for the prolog and code portions.
if (prolog_ranges.size() == 1 && code_ranges.size() == 1 &&
prolog_ranges[0].end() == code_ranges[0].rva) {
FrameInfo fi = { prolog_ranges[0].rva,
prolog_ranges[0].length + code_ranges[0].length,
prolog_ranges[0].length };
frame_infos.push_back(fi);
} else {
// Otherwise we output the prolog and code frame info independently.
for (size_t i = 0; i < prolog_ranges.size(); ++i) {
FrameInfo fi = { prolog_ranges[i].rva,
prolog_ranges[i].length,
prolog_ranges[i].length };
frame_infos.push_back(fi);
}
for (size_t i = 0; i < code_ranges.size(); ++i) {
FrameInfo fi = { code_ranges[i].rva, code_ranges[i].length, 0 };
frame_infos.push_back(fi);
}
}
for (size_t i = 0; i < frame_infos.size(); ++i) {
const FrameInfo& fi(frame_infos[i]);
fprintf(output_, "STACK WIN %lx %lx %lx %lx %x %lx %lx %lx %lx %d ",
type, fi.rva, fi.code_size, fi.prolog_size,
0 /* 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.m_str);
} 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;
}