mirror of
				https://git.eden-emu.dev/eden-emu/eden.git
				synced 2025-10-26 12:33:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			144 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			144 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.
 |