145 lines
8 KiB
Markdown
145 lines
8 KiB
Markdown
|
# Introduction
|
||
|
|
||
|
This page aims to provide a detailed description of how Breakpad produces stack
|
||
|
traces from the information contained within a minidump file.
|
||
|
|
||
|
# Details
|
||
|
|
||
|
## Starting the Process
|
||
|
|
||
|
Typically the stack walking process is initiated by instantiating the
|
||
|
[MinidumpProcessor](../src/processor/minidump_processor.cc)
|
||
|
class and calling the [MinidumpProcessor::Process](../src/processor/minidump_processor.cc#61)
|
||
|
method, providing it a minidump file to process. To produce a useful stack
|
||
|
trace, the MinidumpProcessor requires two other objects which are passed in its
|
||
|
constructor: a [SymbolSupplier](../src/google_breakpad/processor/symbol_supplier.h)
|
||
|
and a [SourceLineResolverInterface](../src/google_breakpad/processor/source_line_resolver_interface.h).
|
||
|
The SymbolSupplier object is responsible for locating and providing SymbolFiles
|
||
|
that match modules from the minidump. The SourceLineResolverInterface is
|
||
|
responsible for loading the symbol files and using the information contained
|
||
|
within to provide function and source information for stack frames, as well as
|
||
|
information on how to unwind from a stack frame to its caller. More detail will
|
||
|
be provided on these interactions later.
|
||
|
|
||
|
A number of data streams are extracted from the minidump to begin stack walking:
|
||
|
the list of threads from the process
|
||
|
([MinidumpThreadList](../src/google_breakpad/processor/minidump.h#335)),
|
||
|
the list of modules loaded in the process
|
||
|
([MinidumpModuleList](../src/google_breakpad/processor/minidump.h#501)),
|
||
|
and information about the exception that caused the process to crash
|
||
|
([MinidumpException](../src/google_breakpad/processor/minidump.h#615)).
|
||
|
|
||
|
## Enumerating Threads
|
||
|
|
||
|
For each thread in the thread list
|
||
|
([MinidumpThread](../src/google_breakpad/processor/minidump.h#299)),
|
||
|
the thread memory containing the stack for the thread
|
||
|
([MinidumpMemoryRegion](../src/google_breakpad/processor/minidump.h#236))
|
||
|
and the CPU context representing the CPU state of the thread at the time the
|
||
|
dump was written ([MinidumpContext](../src/google_breakpad/processor/minidump.h#171))
|
||
|
are extracted from the minidump. If the thread being processed is the thread
|
||
|
that produced the exception then a CPU context is obtained from the
|
||
|
MinidumpException object instead, which represents the CPU state of the thread
|
||
|
at the point of the exception. A stack walker is then instantiated by calling
|
||
|
the [Stackwalker::StackwalkerForCPU](../src/google_breakpad/processor/stackwalker.h#77)
|
||
|
method and passing it the CPU context, the thread memory, the module list, as
|
||
|
well as the SymbolSupplier and SourceLineResolverInterface. This method selects
|
||
|
the specific !Stackwalker subclass based on the CPU architecture of the provided
|
||
|
CPU context and returns an instance of that subclass.
|
||
|
|
||
|
## Walking a thread's stack
|
||
|
|
||
|
Once a !Stackwalker instance has been obtained, the processor calls the
|
||
|
[Stackwalker::Walk](../src/google_breakpad/processor/source_line_resolver_interface.h)
|
||
|
method to obtain a list of frames representing the stack of this thread. The
|
||
|
!Stackwalker starts by calling the GetContextFrame method which returns a
|
||
|
StackFrame representing the top of the stack, with CPU state provided by the
|
||
|
initial CPU context. From there, the stack walker repeats the following steps
|
||
|
for each frame in turn:
|
||
|
|
||
|
### Finding the Module
|
||
|
|
||
|
The address of the instruction pointer of the current frame is used to determine
|
||
|
which module contains the current frame by calling the module list's
|
||
|
[GetModuleForAddress](../src/google_breakpad/processor/code_modules.h#56) method.
|
||
|
|
||
|
### Locating Symbols
|
||
|
|
||
|
If a module is located, the SymbolSupplier is asked to locate symbols
|
||
|
corresponding to the module by calling its
|
||
|
[GetCStringSymbolData](../src/google_breakpad/processor/symbol_supplier.h#87)
|
||
|
method. Typically this is implemented by using the module's debug filename (the
|
||
|
PDB filename for Windows dumps) and debug identifier (a GUID plus one extra
|
||
|
digit) as a lookup key. The [SimpleSymbolSupplier](../src/processor/simple_symbol_supplier.cc)
|
||
|
class simply uses these as parts of a file path to locate a flat file on disk.
|
||
|
|
||
|
### Loading Symbols
|
||
|
|
||
|
If a symbol file is located, the SourceLineResolverInterface is then asked to
|
||
|
load the symbol file by calling its
|
||
|
[LoadModuleUsingMemoryBuffer](../src/google_breakpad/processor/source_line_resolver_interface.h#71)
|
||
|
method. The [BasicSourceLineResolver](../src/processor/basic_source_line_resolver.cc)
|
||
|
implementation parses the text-format [symbol file](symbol_files.md) into
|
||
|
in-memory data structures to make lookups by address of function names, source
|
||
|
line information, and unwind information easy.
|
||
|
|
||
|
### Getting source line information
|
||
|
|
||
|
If a symbol file has been successfully loaded, the SourceLineResolverInterface's
|
||
|
[FillSourceLineInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#89)
|
||
|
method is called to provide a function name and source line information for the
|
||
|
current frame. This is done by subtracting the base address of the module
|
||
|
containing the current frame from the instruction pointer of the current frame
|
||
|
to obtain a relative virtual address (RVA), which is a code offset relative to
|
||
|
the start of the module. This RVA is then used as a lookup into a table of
|
||
|
functions ([FUNC lines](SymbolFiles#FUNC_records.md) from the symbol file), each
|
||
|
of which has an associated address range (function start address, function
|
||
|
size). If a function is found whose address range contains the RVA, then its
|
||
|
name is used. The RVA is then used as a lookup into a table of source lines
|
||
|
([line records](SymbolFiles#Line_records.md) from the symbol file), each of
|
||
|
which also has an associated address range. If a match is found it will provide
|
||
|
the file name and source line associated with the current frame. If no match was
|
||
|
found in the function table, another table of publicly exported symbols may be
|
||
|
consulted ([PUBLIC lines](SymbolFiles#PUBLIC_records.md) from the symbol file).
|
||
|
Public symbols contain only a start address, so the lookup simply looks for the
|
||
|
nearest symbol that is less than the provided RVA.
|
||
|
|
||
|
### Finding the caller frame
|
||
|
|
||
|
To find the next frame in the stack, the !Stackwalker calls its
|
||
|
[GetCallerFrame](../src/google_breakpad/processor/stackwalker.h#186)
|
||
|
method, passing in the current frame. Each !Stackwalker subclass implements
|
||
|
GetCallerFrame differently, but there are common patterns.
|
||
|
|
||
|
Typically the first step is to query the SourceLineResolverInterface for the
|
||
|
presence of detailed unwind information. This is done using its
|
||
|
[FindWindowsFrameInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#96)
|
||
|
and [FindCFIFrameInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#102)
|
||
|
methods. These methods look for Windows unwind info extracted from a PDB file
|
||
|
([STACK WIN](SymbolFiles#STACK_WIN_records.md) lines from the symbol file), or
|
||
|
DWARF CFI extracted from a binary ([STACK CFI](SymbolFiles#STACK_CFI_records.md)
|
||
|
lines from the symbol file) respectively. The information covers address ranges,
|
||
|
so the RVA of the current frame is used for lookup as with function and source
|
||
|
line information.
|
||
|
|
||
|
If unwind info is found it provides a set of rules to recover the register state
|
||
|
of the caller frame given the current register state as well as the thread's
|
||
|
stack memory. The rules are evaluated to produce the caller frame.
|
||
|
|
||
|
If unwind info is not found then the !Stackwalker may resort to other methods.
|
||
|
Typically on architectures which specify a frame pointer unwinding by
|
||
|
dereferencing the frame pointer is tried next. If that is successful it is used
|
||
|
to produce the caller frame.
|
||
|
|
||
|
If no caller frame was found by any other method most !Stackwalker
|
||
|
implementations resort to stack scanning by looking at each word on the stack
|
||
|
down to a fixed depth (implemented in the
|
||
|
[Stackwalker::ScanForReturnAddress](../src/google_breakpad/processor/stackwalker.h#131)
|
||
|
method) and using a heuristic to attempt to find a reasonable return address
|
||
|
(implemented in the
|
||
|
[Stackwalker::InstructionAddressSeemsValid](../src/google_breakpad/processor/stackwalker.h#111) method).
|
||
|
|
||
|
If no caller frame is found or the caller frame seems invalid, stack walking
|
||
|
stops. If a caller frame was found then these steps repeat using the new frame
|
||
|
as the current frame.
|