aboutsummaryrefslogtreecommitdiff
path: root/externals/breakpad/docs/stack_walking.md
diff options
context:
space:
mode:
Diffstat (limited to 'externals/breakpad/docs/stack_walking.md')
-rw-r--r--externals/breakpad/docs/stack_walking.md144
1 files changed, 144 insertions, 0 deletions
diff --git a/externals/breakpad/docs/stack_walking.md b/externals/breakpad/docs/stack_walking.md
new file mode 100644
index 0000000000..9411159464
--- /dev/null
+++ b/externals/breakpad/docs/stack_walking.md
@@ -0,0 +1,144 @@
+# 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.