From 0ff15b41edf1dfd50776877edce7cae6e757574f Mon Sep 17 00:00:00 2001 From: Andy Bonventre Date: Tue, 22 Sep 2015 17:29:52 -0400 Subject: [PATCH] [Docs] add markdown docs (converted from Wiki) BUG=none R=mark CC=google-breakpad-dev@googlegroups.com Review URL: https://codereview.chromium.org/1357773004 . Patch from Andy Bonventre . --- README | 37 - README.md | 47 ++ docs/OWNERS | 1 + docs/breakpad.png | Bin 0 -> 84153 bytes docs/breakpad.svg | 1023 +++++++++++++++++++++++++ docs/client_design.md | 224 ++++++ docs/contributing_to_breakpad.md | 35 + docs/exception_handling.md | 128 ++++ docs/getting_started_with_breakpad.md | 121 +++ docs/linux_starter_guide.md | 97 +++ docs/linux_system_calls.md | 47 ++ docs/mac_breakpad_starter_guide.md | 184 +++++ docs/mozilla_brown_bag_talk.md | 84 ++ docs/processor_design.md | 230 ++++++ docs/stack_walking.md | 160 ++++ docs/symbol_files.md | 497 ++++++++++++ docs/windows_client_integration.md | 70 ++ 17 files changed, 2948 insertions(+), 37 deletions(-) delete mode 100644 README create mode 100644 README.md create mode 100644 docs/OWNERS create mode 100644 docs/breakpad.png create mode 100644 docs/breakpad.svg create mode 100644 docs/client_design.md create mode 100644 docs/contributing_to_breakpad.md create mode 100644 docs/exception_handling.md create mode 100644 docs/getting_started_with_breakpad.md create mode 100644 docs/linux_starter_guide.md create mode 100644 docs/linux_system_calls.md create mode 100644 docs/mac_breakpad_starter_guide.md create mode 100644 docs/mozilla_brown_bag_talk.md create mode 100644 docs/processor_design.md create mode 100644 docs/stack_walking.md create mode 100644 docs/symbol_files.md create mode 100644 docs/windows_client_integration.md diff --git a/README b/README deleted file mode 100644 index ee48714b..00000000 --- a/README +++ /dev/null @@ -1,37 +0,0 @@ -Breakpad is a set of client and server components which implement a -crash-reporting system. - - ------ -Getting started in 32-bit mode (from trunk) -Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure - Build: make - Test: make check - Install: make install - -If you need to reconfigure your build be sure to run "make distclean" first. - - ------ -To request change review: -0. Get a copy of depot_tools repo. - http://dev.chromium.org/developers/how-tos/install-depot-tools - -1. Create a new directory for checking out the source code. - mkdir breakpad && cd breakpad - -2. Run the `fetch` tool from depot_tools to download all the source repos. - fetch breakpad - -3. Make changes. Build and test your changes. - For core code like processor use methods above. - For linux/mac/windows, there are test targets in each project file. - -4. Commit your changes to your local repo and upload them to the server. - http://dev.chromium.org/developers/contributing-code - e.g. git commit ... && git cl upload ... - You will be prompted for credential and a description. - -5. At https://codereview.chromium.org/ you'll find your issue listed; click on - it, and select Publish+Mail, and enter in the code reviewer and CC - google-breakpad-dev@googlegroups.com diff --git a/README.md b/README.md new file mode 100644 index 00000000..bfb9f142 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Breakpad + +Breakpad is a set of client and server components which implement a +crash-reporting system. + +## Getting started in 32-bit mode (from trunk) + +```sh +# Configure +CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure +# Build +make +# Test +make check +# Install +make install +``` + +If you need to reconfigure your build be sure to run `make distclean` first. + +## To request change review: + +1. Get a copy of depot_tools repo. + http://dev.chromium.org/developers/how-tos/install-depot-tools + +2. Create a new directory for checking out the source code. + mkdir breakpad && cd breakpad + +3. Run the `fetch` tool from depot_tools to download all the source repos. + `fetch breakpad` + +4. Make changes. Build and test your changes. + For core code like processor use methods above. + For linux/mac/windows, there are test targets in each project file. + +5. Commit your changes to your local repo and upload them to the server. + http://dev.chromium.org/developers/contributing-code + e.g. `git commit ... && git cl upload ...` + You will be prompted for credential and a description. + +6. At https://codereview.chromium.org/ you'll find your issue listed; click on + it, and select Publish+Mail, and enter in the code reviewer and CC + google-breakpad-dev@googlegroups.com + +## Documentation + +Visit https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/ diff --git a/docs/OWNERS b/docs/OWNERS new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/docs/OWNERS @@ -0,0 +1 @@ +* diff --git a/docs/breakpad.png b/docs/breakpad.png new file mode 100644 index 0000000000000000000000000000000000000000..20bc7566ce853d7d6bbe4e8af820a43e09095c9f GIT binary patch literal 84153 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

Dn+UmXymPW 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 literal 0 HcmV?d00001 diff --git a/docs/breakpad.svg b/docs/breakpad.svg new file mode 100644 index 00000000..e91e72cb --- /dev/null +++ b/docs/breakpad.svg @@ -0,0 +1,1023 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + Breakpad Client + Debugging Information + Application Code + + Build System + User's System + Crash Collector + + + + + + + + + + + + + + + + + + + + + Minidump + + + + Minidump + + + + + + + + + + + Human-readableStack Trace + + strip debuginfo + + Breakpad symboldumper + + distribute program to users + + + copy symbol file + + Crash! Breakpad clientwrites minidump... + + ...and submits itto crash collector + Breakpadminidumpprocessor + + + + + + + diff --git a/docs/client_design.md b/docs/client_design.md new file mode 100644 index 00000000..30ffb2fb --- /dev/null +++ b/docs/client_design.md @@ -0,0 +1,224 @@ +# Breakpad Client Libraries + +## Objective + +The Breakpad client libraries are responsible for monitoring an application for +crashes (exceptions), handling them when they occur by generating a dump, and +providing a means to upload dumps to a crash reporting server. These tasks are +divided between the “handler” (short for “exception handler”) library linked in +to an application being monitored for crashes, and the “sender” library, +intended to be linked in to a separate external program. + +## Background + +As one of the chief tasks of the client handler is to generate a dump, an +understanding of [dump files](processor_design.md) will aid in understanding the +handler. + +## Overview + +Breakpad provides client libraries for each of its target platforms. Currently, +these exist for Windows on x86 and Mac OS X on both x86 and PowerPC. A Linux +implementation has been written and is currently under review. + +Because the mechanisms for catching exceptions and the methods for obtaining the +information that a dump contains vary between operating systems, each target +operating system requires a completely different handler implementation. Where +multiple CPUs are supported for a single operating system, the handler +implementation will likely also require separate code for each processor type to +extract CPU-specific information. One of the goals of the Breakpad handler is to +provide a prepackaged cross-platform system that masks many of these +system-level differences and quirks from the application developer. Although the +underlying implementations differ, the handler library for each system follows +the same set of principles and exposes a similar interface. + +Code that wishes to take advantage of Breakpad should be linked against the +handler library, and should, at an appropriate time, install a Breakpad handler. +For applications, it is generally desirable to install the handler as early in +the start-up process as possible. Developers of library code using Breakpad to +monitor itself may wish to install a Breakpad handler when the library is +loaded, or may only want to install a handler when calls are made in to the +library. + +The handler can be triggered to generate a dump either by catching an exception +or at the request of the application itself. The latter case may be useful in +debugging assertions or other conditions where developers want to know how a +program got in to a specific non-crash state. After generating a dump, the +handler calls a user-specified callback function. The callback function may +collect additional data about the program’s state, quit the program, launch a +crash reporter application, or perform other tasks. Allowing for this +functionality to be dictated by a callback function preserves flexibility. + +The sender library is also has a separate implementation for each supported +platform, because of the varying interfaces for accessing network resources on +different operating systems. The sender transmits a dump along with other +application-defined information to a crash report server via HTTP. Because dumps +may contain sensitive data, the sender allows for the use of HTTPS. + +The canonical example of the entire client system would be for a monitored +application to link against the handler library, install a Breakpad handler from +its main function, and provide a callback to launch a small crash reporter +program. The crash reporter program would be linked against the sender library, +and would send the crash dump when launched. A separate process is recommended +for this function because of the unreliability inherent in doing any significant +amount of work from a crashed process. + +## Detailed Design + +### Exception Handler Installation + +The mechanisms for installing an exception handler vary between operating +systems. On Windows, it’s a relatively simple matter of making one call to +register a [top-level exception filter] +(http://msdn.microsoft.com/library/en-us/debug/base/setunhandledexceptionfilter.asp) +callback function. On most Unix-like systems such as Linux, processes are +informed of exceptions by the delivery of a signal, so an exception handler +takes the form of a signal handler. The native mechanism to catch exceptions on +Mac OS X requires a large amount of code to set up a Mach port, identify it as +the exception port, and assign a thread to listen for an exception on that port. +Just as the preparation of exception handlers differ, the manner in which they +are called differs as well. On Windows and most Unix-like systems, the handler +is called on the thread that caused the exception. On Mac OS X, the thread +listening to the exception port is notified that an exception has occurred. The +different implementations of the Breakpad handler libraries perform these tasks +in the appropriate ways on each platform, while exposing a similar interface on +each. + +A Breakpad handler is embodied in an `ExceptionHandler` object. Because it’s a +C++ object, `ExceptionHandler`s may be created as local variables, allowing them +to be installed and removed as functions are called and return. This provides +one possible way for a developer to monitor only a portion of an application for +crashes. + +### Exception Basics + +Once an application encounters an exception, it is in an indeterminate and +possibly hazardous state. Consequently, any code that runs after an exception +occurs must take extreme care to avoid performing operations that might fail, +hang, or cause additional exceptions. This task is not at all straightforward, +and the Breakpad handler library seeks to do it properly, accounting for all of +the minute details while allowing other application developers, even those with +little systems programming experience, to reap the benefits. All of the Breakpad +handler code that executes after an exception occurs has been written according +to the following guidelines for safety at exception time: + +* Use of the application heap is forbidden. The heap may be corrupt or + otherwise unusable, and allocators may not function. +* Resource allocation must be severely limited. The handler may create a new + file to contain the dump, and it may attempt to launch a process to continue + handling the crash. +* Execution on the thread that caused the exception is significantly limited. + The only code permitted to execute on this thread is the code necessary to + transition handling to a dedicated preallocated handler thread, and the code + to return from the exception handler. +* Handlers shouldn’t handle crashes by attempting to walk stacks themselves, + as stacks may be in inconsistent states. Dump generation should be performed + by interfacing with the operating system’s memory manager and code module + manager. +* Library code, including runtime library code, must be avoided unless it + provably meets the above guidelines. For example, this means that the STL + string class may not be used, because it performs operations that attempt to + allocate and use heap memory. It also means that many C runtime functions + must be avoided, particularly on Windows, because of heap operations that + they may perform. + +A dedicated handler thread is used to preserve the state of the exception thread +when an exception occurs: during dump generation, it is difficult if not +impossible for a thread to accurately capture its own state. Performing all +exception-handling functions on a separate thread is also critical when handling +stack-limit-exceeded exceptions. It would be hazardous to run out of stack space +while attempting to handle an exception. Because of the rule against allocating +resources at exception time, the Breakpad handler library creates its handler +thread when it installs its exception handler. On Mac OS X, this handler thread +is created during the normal setup of the exception handler, and the handler +thread will be signaled directly in the event of an exception. On Windows and +Linux, the handler thread is signaled by a small amount of code that executes on +the exception thread. Because the code that executes on the exception thread in +this case is small and safe, this does not pose a problem. Even when an +exception is caused by exceeding stack size limits, this code is sufficiently +compact to execute entirely within the stack’s guard page without causing an +exception. + +The handler thread may also be triggered directly by a user call, even when no +exception occurs, to allow dumps to be generated at any point deemed +interesting. + +### Filter Callback + +When the handler thread begins handling an exception, it calls an optional +user-defined filter callback function, which is responsible for judging whether +Breakpad’s handler should continue handling the exception or not. This mechanism +is provided for the benefit of library or plug-in code, whose developers may not +be interested in reports of crashes that occur outside of their modules but +within processes hosting their code. If the filter callback indicates that it is +not interested in the exception, the Breakpad handler arranges for it to be +delivered to any previously-installed handler. + +### Dump Generation + +Assuming that the filter callback approves (or does not exist), the handler +writes a dump in a directory specified by the application developer when the +handler was installed, using a previously generated unique identifier to avoid +name collisions. The mechanics of dump generation also vary between platforms, +but in general, the process involves enumerating each thread of execution, and +capturing its state, including processor context and the active portion of its +stack area. The dump also includes a list of the code modules loaded in to the +application, and an indicator of which thread generated the exception or +requested the dump. In order to avoid allocating memory during this process, the +dump is written in place on disk. + +### Post-Dump Behavior + +Upon completion of writing the dump, a second callback function is called. This +callback may be used to launch a separate crash reporting program or to collect +additional data from the application. The callback may also be used to influence +whether Breakpad will treat the exception as handled or unhandled. Even after a +dump is successfully generated, Breakpad can be made to behave as though it +didn’t actually handle an exception. This function may be useful for developers +who want to test their applications with Breakpad enabled but still retain the +ability to use traditional debugging techniques. It also allows a +Breakpad-enabled application to coexist with a platform’s native crash reporting +system, such as Mac OS X’ [CrashReporter] +(http://developer.apple.com/technotes/tn2004/tn2123.html) and [Windows Error +Reporting](http://msdn.microsoft.com/isv/resources/wer/). + +Typically, when Breakpad handles an exception fully and no debuggers are +involved, the crashed process will terminate. + +Authors of both callback functions that execute within a Breakpad handler are +cautioned that their code will be run at exception time, and that as a result, +they should observe the same programming practices that the Breakpad handler +itself adheres to. Notably, if a callback is to be used to collect additional +data from an application, it should take care to read only “safe” data. This +might involve accessing only static memory locations that are updated +periodically during the course of normal program execution. + +### Sender Library + +The Breakpad sender library provides a single function to send a crash report to +a crash server. It accepts a crash server’s URL, a map of key-value parameters +that will accompany the dump, and the path to a dump file itself. Each of the +key-value parameters and the dump file are sent as distinct parts of a multipart +HTTP POST request to the specified URL using the platform’s native HTTP +facilities. On Linux, [libcurl](http://curl.haxx.se/) is used for this function, +as it is the closest thing to a standard HTTP library available on that +platform. + +## Future Plans + +Although we’ve had great success with in-process dump generation by following +our guidelines for safe code at exception time, we are exploring options for +allowing dumps to be generated in a separate process, to further enhance the +handler library’s robustness. + +On Windows, we intend to offer tools to make it easier for Breakpad’s settings +to be managed by the native group policy management system. + +We also plan to offer tools that many developers would find desirable in the +context of handling crashes, such as a mechanism to determine at launch if the +program last terminated in a crash, and a way to calculate “crashiness” in terms +of crashes over time or the number of application launches between crashes. + +We are also investigating methods to capture crashes that occur early in an +application’s launch sequence, including crashes that occur before a program’s +main function begins executing. diff --git a/docs/contributing_to_breakpad.md b/docs/contributing_to_breakpad.md new file mode 100644 index 00000000..b8d261e9 --- /dev/null +++ b/docs/contributing_to_breakpad.md @@ -0,0 +1,35 @@ +# Introduction + +Thanks for thinking of contributing to Breakpad! Unfortunately there are some +pesky legal issues to get out of the way, but they're quick and painless. + +## Legal + +If you're doing work individually, not as part of any employment, you'll need to +sign the Individual +Contributor License Agreement. This agreement can be completed +electronically. + +If you're contributing to Breakpad as part of your employment with another +organization, you'll need to sign a Corporate +Contributor License Agreement. Once completed this document will need to be +faxed. + +**_IMPORTANT_**: The authors(you!) of the contributions will maintain all +copyrights; the agreements you sign will grant rights to Google to use your +work. + +Thanks, and if you have any questions let me know and I'll loop in the legal guy +here to get you an answer. + +## Technical + +Once you have signed the agreement you can be added to our contributors list and +have write access to code. For full details on getting started see our trunk +`README`. + +## List of people who have signed contributor agreements + +None so far. diff --git a/docs/exception_handling.md b/docs/exception_handling.md new file mode 100644 index 00000000..e48a52ae --- /dev/null +++ b/docs/exception_handling.md @@ -0,0 +1,128 @@ +The goal of this document is to give an overview of the exception handling +options in breakpad. + +# Basics + +Exception handling is a mechanism designed to handle the occurrence of +exceptions, special conditions that change the normal flow of program execution. + +`SetUnhandledExceptionFilter` replaces all unhandled exceptions when Breakpad is +enabled. TODO: More on first and second change and vectored v. try/catch. + +There are two main types of exceptions across all platforms: in-process and +out-of-process. + +# In-Process + +In process exception handling is relatively simple since the crashing process +handles crash reporting. It is generally considered unsafe to write a minidump +from a crashed process. For example, key data structures could be corrupted or +the stack on which the exception handler runs could have been overwritten. For +this reason all platforms also support some level of out-of-process exception +handling. + +## Windows + +In-process exception handling Breakpad creates a 'handler head' that waits +infinitely on a semaphore at start up. When this thread is woken it writes the +minidump and signals to the excepting thread that it may continue. A filter will +tell the OS to kill the process if the minidump is written successfully. +Otherwise it continues. + +# Out-of-Process + +Out-of-process exception handling is more complicated than in-process exception +handling because of the need to set up a separate process that can read the +state of the crashing process. + +## Windows + +Breakpad uses two abstractions around the exception handler to make things work: +`CrashGenerationServer` and `CrashGenerationClient`. The constructor for these +takes a named pipe name. + +During server start up a named pipe and registers callbacks for client +connections are created. The named pipe is used for registration and all IO on +the pipe is done asynchronously. `OnPipeConnected` is called when a client +attempts to connect (call `CreateFile` on the pipe). `OnPipeConnected` does the +state machine transition from `Initial` to `Connecting` and on through +`Reading`, `Reading_Done`, `Writing`, `Writing_Done`, `Reading_ACK`, and +`Disconnecting`. + +When registering callbacks, the client passes in two pointers to pointers: 1. A +pointer to the `EXCEPTION_INFO` pointer 1. A pointer to the `MDRawAssertionInfo` +which handles various non-exception failures like assertions + +The essence of registration is adding a "`ClientInfo`" object that contains +handles used for synchronization with the crashing process to an array +maintained by the server. This is how we can keep track of all the clients on +the system that have registered for minidumps. These handles are: * +`server_died(mutex)` * `dump_requested(Event)` * `dump_generated(Event)` + +The server registers asynchronous waits on these events with the `ClientInfo` +object as the callback context. When the `dump_requested` event is set by the +client, the `OnDumpRequested()` callback is called. The server uses the handles +inside `ClientInfo` to communicate with the child process. Once the child sets +the event, it waits for two objects: 1. the `dump_generated` event 1. the +`server_died` mutex + +In the end handles are "duped" into the client process, and the clients use +`SetEvent` to request events, wait on the other event, or the `server_died` +mutex. + +## Linux + +### Current Status + +As of July 2011, Linux had a minidump generator that is not entirely +out-of-process. The minidump was generated from a separate process, but one that +shared an address space, file descriptors, signal handles and much else with the +crashing process. It worked by using the `clone()` system call to duplicate the +crashing process, and then uses `ptrace()` and the `/proc` file system to +retrieve the information required to write the minidump. Since then Breakpad has +updated Linux exception handling to provide more benefits of out-of-process +report generation. + +### Proposed Design + +#### Overview + +Breakpad would use a per-user daemon to write out a minidump that does not have, +interact with or depend on the crashing process. We don't want to start a new +separate process every time a user launches a Breakpad-enabled process. Doing +one daemon per machine is unacceptable for security concerns around one user +being able to initiate a minidump generation for another user's process. + +#### Client/Server Communication + +On Breakpad initialization in a process, the initializer would check if the +daemon is running and, if not, start it. The race condition between the check +and the initialization is not a problem because multiple daemons can check if +the IPC endpoint already exists and if a server is listening. Even if multiple +copies of the daemon try to `bind()` the filesystem to name the socket, all but +one will fail and can terminate. + +This point is relevant for error handling conditions. Linux does not clean the +file system representation of a UNIX domain socket even if both endpoints +terminate, so checking for existence is not strong enough. However checking the +process list or sending a ping on the socket can handle this. + +Breakpad uses UNIX domain sockets since they support full duplex communication +(unlike Windows, named pipes on Linux are half) and the kernal automatically +creates a private channel between the client and server once the client calls +`connect()`. + +#### Minidump Generation + +Breakpad could use the current system with `ptrace()` and `/proc` within the +daemon executable. + +Overall the operations look like: 1. Signal from OS indicating crash 1. Signal +Handler suspends all threads except itself 1. Signal Handler sends +`CRASH_DUMP_REQUEST` message to server and waits for response 1. Server inspects +1. Minidump is asynchronously written to disk by the server 1. Server responds +indicating inspection is done + +## Mac OSX + +Out-of-process exception handling is fully supported on Mac. diff --git a/docs/getting_started_with_breakpad.md b/docs/getting_started_with_breakpad.md new file mode 100644 index 00000000..a41b5406 --- /dev/null +++ b/docs/getting_started_with_breakpad.md @@ -0,0 +1,121 @@ +# Introduction + +Breakpad is a library and tool suite that allows you to distribute an +application to users with compiler-provided debugging information removed, +record crashes in compact "minidump" files, send them back to your server, and +produce C and C++ stack traces from these minidumps. Breakpad can also write +minidumps on request for programs that have not crashed. + +Breakpad is currently used by Google Chrome, Firefox, Google Picasa, Camino, +Google Earth, and other projects. + +![http://google-breakpad.googlecode.com/svn/wiki/breakpad.png] +(http://google-breakpad.googlecode.com/svn/wiki/breakpad.png) + +Breakpad has three main components: + +* The **client** is a library that you include in your application. It can + write minidump files capturing the current threads' state and the identities + of the currently loaded executable and shared libraries. You can configure + the client to write a minidump when a crash occurs, or when explicitly + requested. + +* The **symbol dumper** is a program that reads the debugging information + produced by the compiler and produces a **symbol file**, in [Breakpad's own + format](symbol_files.md). + +* The **processor** is a program that reads a minidump file, finds the + appropriate symbol files for the versions of the executables and shared + libraries the minidump mentions, and produces a human-readable C/C++ stack + trace. + +# The minidump file format + +The minidump file format is similar to core files but was developed by Microsoft +for its crash-uploading facility. A minidump file contains: + +* A list of the executable and shared libraries that were loaded in the + process at the time the dump was created. This list includes both file names + and identifiers for the particular versions of those files that were loaded. + +* A list of threads present in the process. For each thread, the minidump + includes the state of the processor registers, and the contents of the + threads' stack memory. These data are uninterpreted byte streams, as the + Breakpad client generally has no debugging information available to produce + function names or line numbers, or even identify stack frame boundaries. + +* Other information about the system on which the dump was collected: + processor and operating system versions, the reason for the dump, and so on. + +Breakpad uses Windows minidump files on all platforms, instead of the +traditional core files, for several reasons: + +* Core files can be very large, making them impractical to send across a + network to the collector for processing. Minidumps are smaller, as they were + designed to be used this way. + +* The core file format is poorly documented. For example, the Linux Standards + Base does not describe how registers are stored in `PT_NOTE` segments. + +* It is harder to persuade a Windows machine to produce a core dump file than + it is to persuade other machines to write a minidump file. + +* It simplifies the Breakpad processor to support only one file format. + +# Overview/Life of a minidump + +A minidump is generated via calls into the Breakpad library. By default, +initializing Breakpad installs an exception/signal handler that writes a +minidump to disk at exception time. On Windows, this is done via +`SetUnhandledExceptionFilter()`; on OS X, this is done by creating a thread that +waits on the Mach exception port; and on Linux, this is done by installing a +signal handler for various exceptions like `SIGILL, SIGSEGV` etc. + +Once the minidump is generated, each platform has a slightly different way of +uploading the crash dump. On Windows & Linux, a separate library of functions is +provided that can be called into to do the upload. On OS X, a separate process +is spawned that prompts the user for permission, if configured to do so, and +sends the file. + +# Terminology + +**In-process vs. out-of-process exception handling** - it's generally considered +that writing the minidump from within the crashed process is unsafe - key +process data structures could be corrupted, or the stack on which the exception +handler runs could have been overwritten, etc. All 3 platforms support what's +known as "out-of-process" exception handling. + +# Integration overview + +## Breakpad Code Overview + +All the client-side code is found by visiting the Google Project at +http://code.google.com/p/google-breakpad. The following directory structure is +present in the `src` directory: + +* `processor` Contains minidump-processing code that is used on the server + side and isn't of use on the client side +* `client` Contains client minidump-generation libraries for all platforms +* `tools` Contains source code & projects for building various tools on each + platform. + +(Among other directories) + +* Windows + Integration Guide +* Mac + Integration Guide +* + Linux Integration Guide + +## Build process specifics(symbol generation) + +This applies to all platforms. Inside `src/tools/{platform}/dump_syms` is a tool +that can read debugging information for each platform (e.g. for OS X/Linux, +DWARF and STABS, and for Windows, PDB files) and generate a Breakpad symbol +file. This tool should be run on your binary before it's stripped(in the case of +OS X/Linux) and the symbol files need to be stored somewhere that the minidump +processor can find. There is another tool, `symupload`, that can be used to +upload symbol files if you have written a server that can accept them. diff --git a/docs/linux_starter_guide.md b/docs/linux_starter_guide.md new file mode 100644 index 00000000..9ab48624 --- /dev/null +++ b/docs/linux_starter_guide.md @@ -0,0 +1,97 @@ +# How To Add Breakpad To Your Linux Application + +This document is an overview of using the Breakpad client libraries on Linux. + +## Building the Breakpad libraries + +Breakpad provides an Autotools build system that will build both the Linux +client libraries and the processor libraries. Running `./configure && make` in +the Breakpad source directory will produce +**src/client/linux/libbreakpad\_client.a**, which contains all the code +necessary to produce minidumps from an application. + +## Integrating Breakpad into your Application + +First, configure your build process to link **libbreakpad\_client.a** into your +binary, and set your include paths to include the **src** directory in the +**google-breakpad** source tree. Next, include the exception handler header: ``` + +# include "client/linux/handler/exception_handler.h" + +``` + +Now you can instantiate an `ExceptionHandler` object. Exception handling is active for the lifetime of the `ExceptionHandler` object, so you should instantiate it as early as possible in your application's startup process, and keep it alive for as close to shutdown as possible. To do anything useful, the `ExceptionHandler` constructor requires a path where it can write minidumps, as well as a callback function to receive information about minidumps that were written: +``` + +static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, +void* context, bool succeeded) { printf("Dump path: %s\n", descriptor.path()); +return succeeded; } + +void crash() { volatile int* a = (int*)(NULL); *a = 1; } + +int main(int argc, char* argv[]) { google_breakpad::MinidumpDescriptor +descriptor("/tmp"); google_breakpad::ExceptionHandler eh(descriptor, NULL, +dumpCallback, NULL, true, -1); crash(); return 0; } ``` + +Compiling and running this example should produce a minidump file in /tmp, and +it should print the minidump filename before exiting. You can read more about +the other parameters to the `ExceptionHandler` constructor in +the exception_handler.h source file. + +**Note**: You should do as little work as possible in the callback function. +Your application is in an unsafe state. It may not be safe to allocate memory or +call functions from other shared libraries. The safest thing to do is `fork` and +`exec` a new process to do any work you need to do. If you must do some work in +the callback, the Breakpad source contains some +simple reimplementations of libc functions, to avoid calling directly into +libc, as well as a +header file for making Linux system calls (in **src/third\_party/lss**) to +avoid calling into other shared libraries. + +## Sending the minidump file + +In a real application, you would want to handle the minidump in some way, likely +by sending it to a server for analysis. The Breakpad source tree contains some +HTTP upload source that you might find useful, as well as a +minidump upload tool. + +## Producing symbols for your application + +To produce useful stack traces, Breakpad requires you to convert the debugging +symbols in your binaries to text-format +symbol files. First, ensure that you've compiled your binaries with `-g` to +include debugging symbols. Next, compile the `dump_syms` tool by running +`configure && make` in the Breakpad source directory. Next, run `dump_syms` on +your binaries to produce the text-format symbols. For example, if your main +binary was named `test`: `$ google-breakpad/src/tools/linux/dump_syms/dump_syms +./test > test.sym +` + +In order to use these symbols with the `minidump_stackwalk` tool, you will need +to place them in a specific directory structure. The first line of the symbol +file contains the information you need to produce this directory structure, for +example (your output will vary): `$ head -n1 test.sym MODULE Linux x86_64 +6EDC6ACDB282125843FD59DA9C81BD830 test $ mkdir -p +./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830 $ mv test.sym +./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830 +` + +You may also find the symbolstore.py +script in the Mozilla repository useful, as it encapsulates these steps. + +## Processing the minidump to produce a stack trace + +Breakpad includes a tool called `minidump_stackwalk` which can take a minidump +plus its corresponding text-format symbols and produce a symbolized stacktrace. +It should be in the **google-breakpad/src/processor** directory if you compiled +the Breakpad source using the directions above. Simply pass it the minidump and +the symbol path as commandline parameters: +`google-breakpad/src/processor/minidump_stackwalk minidump.dmp ./symbols +` It produces verbose output on stderr, and the stacktrace on stdout, so you may +want to redirect stderr. diff --git a/docs/linux_system_calls.md b/docs/linux_system_calls.md new file mode 100644 index 00000000..17ada7e0 --- /dev/null +++ b/docs/linux_system_calls.md @@ -0,0 +1,47 @@ +# Introduction + +Linux implements its userland-to-kernel transition using a special library +called linux-gate.so that is mapped by the kernel into every process. For more +information, see + +http://www.trilithium.com/johan/2005/08/linux-gate/ + +In a nutshell, the problem is that the system call gate function, +kernel\_vsyscall does not use EBP to point to the frame pointer. + +However, the Breakpad processor supports special frames like this via STACK +lines in the symbol file. If you look in src/client/linux/data you will see +symbol files for linux-gate.so for both Intel & AMD(the implementation of +kernel\_vsyscall changes depending on the CPU manufacturer). When processing +minidumps from Linux 2.6, having these symbol files is necessary for walking the +stack for crashes that happen while a thread is in a system call. + +If you're just interested in processing minidumps, those two symbol files should +be all you need! + +# Details + +The particular details of understanding the linux-gate.so symbol files can be +found by reading about STACK lines inside +src/common/windows/pdb\_source\_line\_writer.cc, and the above link. To +summarize briefly, we just have to inform the processor how to get to the +previous frame when the EIP is inside kernel\_vsyscall, and we do that by +telling the processor how many bytes kernel\_vsyscall has pushed onto the stack +in it's prologue. For example, one of the symbol files looks somewhat like the +following: + +MODULE Linux x86 random\_debug\_id linux-gate.so PUBLIC 400 0 kernel\_vsyscall +STACK WIN 4 100 1 1 0 0 0 0 0 1 + +The PUBLIC line indicates that kernel\_vsyscall is at offset 400 (in bytes) from +the beginning of linux-gate.so. The STACK line indicates the size of the +function(100), how many bytes it pushes(1), and how many bytes it pops(1). The +last 1 indicates that EBP is pushed onto the stack before being used by the +function. + +# Warnings + +These functions might change significantly depending on kernel version. In my +opinion, the actual function stack information is unlikely to change frequently, +but the Linux kernel might change the address of kernel\_vsyscall w.r.t the +beginning of linux-gate.so, which would cause these symbol files to be invalid. diff --git a/docs/mac_breakpad_starter_guide.md b/docs/mac_breakpad_starter_guide.md new file mode 100644 index 00000000..6e0bdb0e --- /dev/null +++ b/docs/mac_breakpad_starter_guide.md @@ -0,0 +1,184 @@ +# How To Add Breakpad To Your Mac Client Application + +This document is a step-by-step recipe to get your Mac client app to build with +Breakpad. + +## Preparing a binary build of Breakpad for use in your tree + +You can either check in a binary build of the Breakpad framework & tools or +build it as a dependency of your project. The former is recommended, and +detailed here, since building dependencies through other projects is +problematic(matching up configuration names), and the Breakpad code doesn't +change nearly often enough as your application's will. + +## Building the requisite targets + +All directories are relative to the `src` directory of the Breakpad checkout. + +* Build the 'All' target of `client/mac/Breakpad.xcodeproj` in Release mode. +* Execute `cp -R client/mac/build/Release/Breakpad.framework ` +* Inside `tools/mac/dump_syms` directory, build dump\_syms.xcodeproj, and copy + tools/mac/dump\_syms/build/Release/dump\_syms to a safe location where it + can be run during the build process. + +## Adding Breakpad.framework + +Inside your application's framework, add the Breakpad.Framework to your +project's framework settings. When you select it from the file chooser, it will +let you pick a target to add it to; go ahead and check the one that's relevant +to your application. + +## Copy Breakpad into your Application Package + +Copy Breakpad into your Application Package, so it will be around at run time. + +Go to the Targets section of your Xcode Project window. Hit the disclosure +triangle to reveal the build phases of your application. Add a new Copy Files +phase using the Contextual menu (Control Click). On the General panel of the new +'Get Info' of this new phase, set the destination to 'Frameworks' Close the +'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks' +Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever +it appears in the project file tree. + +## Add a New Run Script build phase + +Near the end of the build phases, add a new Run Script build phase. This will be +run before Xcode calls /usr/bin/strip on your project. This is where you'll be +calling dump\_sym to output the symbols for each architecture of your build. In +my case, the relevant lines read: + +``` +#!/bin/sh +$TOOL_DIR= + +"$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad" + +"$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad" +``` + +## Adjust the Project Settings + +* Turn on Separate Strip, +* Set the Strip Style to Non-Global Symbols. + +## Write Code! + +You'll need to have an object that acts as the delegate for NSApplication. +Inside this object's header, you'll need to add + +1. add an ivar for Breakpad and +2. a declaration for the applicationShouldTerminate:(NSApplication`*` sender) + message. + +``` +#import + +@interface BreakpadTest : NSObject { + . + . + . + BreakpadRef breakpad; + . + . + . +} +. +. +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +. +. +@end +``` + +Inside your object's implementation file, + +1. add the following method InitBreakpad +2. modify your awakeFromNib method to look like the one below, +3. modify/add your application's delegate method to look like the one below + +``` +static BreakpadRef InitBreakpad(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + BreakpadRef breakpad = 0; + NSDictionary *plist = [[NSBundle mainBundle] infoDictionary]; + if (plist) { + // Note: version 1.0.0.4 of the framework changed the type of the argument + // from CFDictionaryRef to NSDictionary * on the next line: + breakpad = BreakpadCreate(plist); + } + [pool release]; + return breakpad; +} + +- (void)awakeFromNib { + breakpad = InitBreakpad(); +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + BreakpadRelease(breakpad); + return NSTerminateNow; +} +``` + +## Configure Breakpad + +Configure Breakpad for your application. + +1. Take a look inside the Breakpad.framework at the Breakpad.h file for the + keys, default values, and descriptions to be passed to BreakpadCreate(). +2. Add/Edit the Breakpad specific entries in the dictionary passed to + BreakpadCreate() -- typically your application's info plist. + +Example from the Notifier Info.plist: +`BreakpadProductGoogle_Notifier_Mac +BreakpadProductDisplay${PRODUCT_NAME} +` + +## Build Your Application + +Almost done! + +## Verify + +Double-check: + +Your app should have in its package contents: +myApp.app/Contents/Frameworks/Breakpad.framework. + +The symbol files have reasonable contents (you can look at them with a text +editor.) + +Look again at the Copy Frameworks phase of your project. Are you leaking .h +files? Select them and delete them. (If you drag a bunch of files into your +project, Xcode often wants to copy your .h files into the build, revealing +Google secrets. Be vigilant!) + +## Upload the symbol file + +You'll need to configure your build process to store symbols in a location that +is accessible by the minidump processor. There is a tool in tools/mac/symupload +that can be used to send the symbol file via HTTP post. + +1. Test + +Configure breakpad to send reports to a URL by adding to your app's Info.plist: + +``` +BreakpadURL +upload URL +BreakpadReportInterval +30 +``` + +## Final Notes + +Breakpad checks whether it is being run under a debugger, and if so, normally +does nothing. But, you can force Breakpad to function under a debugger by +setting the Unix shell variable BREAKPAD\_IGNORE\_DEBUGGER to a non-zero value. +You can bracket the source code in the above Write The Code step with #if DEBUG +to completely eliminate it from Debug builds. See +//depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process +forks(), exception handlers are reset to the default for child processes. So +they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's +Crash Reporter. diff --git a/docs/mozilla_brown_bag_talk.md b/docs/mozilla_brown_bag_talk.md new file mode 100644 index 00000000..8322d244 --- /dev/null +++ b/docs/mozilla_brown_bag_talk.md @@ -0,0 +1,84 @@ +# Breakpad Crash Reporting for Mozilla + +* January 24, 2007 + * Links updated February 14, 2007 +* Mozilla HQ +* Mark Mentovai +* Brian Ryner + +## What is a crash reporter? + +* Enables developers to analyze crashes that occur in the wild +* Produces stack backtraces that help identify how a program failed +* Offers higher-level data aggregation (topcrashes, MTBF statistics) + +## Motivation + +* Talkback is proprietary and unmaintained +* Smaller open-source projects have few options +* Larger projects need flexibility and scalability + +## Design Options + +* Stackwalking done on client + * Apple CrashReporter + * GNOME BugBuddy +* Client sends memory dump + * Talkback + * Windows Error Reporting + * Breakpad + +## Goals + +* Provide libraries around which systems can be based +* Open-source +* Cross-platform + * Mac OS X x86, PowerPC + * Linux x86 + * Windows x86 +* No requirement to distribute symbols + +## Client Libraries + +* Exception handler installed at application startup + * Spawns a separate thread +* Minidump file written at crash time + * Format used by Windows debuggers +* Separate application invoked to send + * HTTP[S](S.md) POST, can include additional parameters + +## Symbols + +* Cross-platform symbol file format +* Contents + * Function names + * Source file names and line numbers + * Windows: Frame pointer omission data + * Future: parameters and local variables +* Symbol conversion methods + +## Processor + +* Examines minidump file and invokes stackwalker +* Symbol files requested from a SymbolSupplier +* Produces stack trace +* Output may be placed where convenient + +## Intergation + +* Breakpad client present in Gran Paradiso Alpha 1 for Windows + * Disabled by default + * Enable with `MOZ_AIRBAG` +* Proof-of-concept collector + * http://mavra.perilith.com/~luser/airbag-collector/list.pl +* Other platforms coming soon + +## More Information + +* Project home: http://code.google.com/p/google-breakpad/ +* Mailing lists + * [google-breakpad-dev@googlegroups.com] + (http://groups.google.com/group/google-breakpad-dev/) + * [google-breakpad-discuss@googlegroups.com] + (http://groups.google.com/group/google-breakpad-discuss/) +* Ask me (irc.mozilla.org: mento) diff --git a/docs/processor_design.md b/docs/processor_design.md new file mode 100644 index 00000000..c2af41a1 --- /dev/null +++ b/docs/processor_design.md @@ -0,0 +1,230 @@ +# Breakpad Processor Library + +## Objective + +The Breakpad processor library is an open-source framework to access the the +information contained within crash dumps for multiple platforms, and to use that +information to produce stack traces showing the call chain of each thread in a +process. After processing, this data is made available to users of the library. + +## Background + +The Breakpad processor is intended to sit at the core of a comprehensive +crash-reporting system that does not require debugging information to be +provided to those running applications being monitored. Some existing +crash-reporting systems, such as [GNOME](http://www.gnome.org/)’s Bug-Buddy and +[Apple](http://www.apple.com/)’s [CrashReporter] +(http://developer.apple.com/technotes/tn2004/tn2123.html), require symbolic +information to be present on the end user’s computer; in the case of +CrashReporter, the reports are transmitted only to Apple, not to third-party +developers. Other systems, such as [Microsoft](http://www.microsoft.com/)’s +[Windows Error Reporting](http://msdn.microsoft.com/isv/resources/wer/) and +SupportSoft’s Talkback, transmit only a snapshot of a crashed process’ state, +which can later be combined with symbolic debugging information without the need +for it to be present on end users’ computers. Because symbolic debugging +information consumes a large amount of space and is otherwise not needed during +the normal operation of software, and because some developers are reluctant to +release debugging symbols to their customers, Breakpad follows the latter +approach. + +We know of no currently-maintained crash-reporting systems that meet our +requirements, which are to: * allow for symbols to be separate from the +application, * handle crash reports from multiple platforms, * allow developers +to operate their own crash-reporting platform, and to * be open-source. Windows +Error Reporting only functions for Microsoft products, and requires the +involvement of Microsoft’s servers. Talkback, while cross-platform, has not been +maintained and at this point does not support Mac OS X on x86, which we consider +to be a significant platform. Talkback is also closed-source commercial +software, and has very specific requirements for its server platform. + +We are aware of Windows-only crash-reporting systems that leverage Microsoft’s +debugging interfaces. Such systems, even if extended to support dumps from other +platforms, are tied to using Windows for at least a portion of the processor +platform. + +## Overview + +The Breakpad processor itself is written in standard C++ and will work on a +variety of platforms. The dumps it accepts may also have been created on a +variety of systems. The library is able to combine dumps with symbolic debugging +information to create stack traces that include function signatures. The +processor library includes simple command-line tools to examine dumps and +process them, producing stack traces. It also exposes several layers of APIs +enabling crash-reporting systems to be built around the Breakpad processor. + +## Detailed Design + +### Dump Files + +In the processor, the dump data is of primary significance. Dumps typically +contain: + +* CPU context (register data) as it was at the time the crash occurred, and an + indication of which thread caused the crash. General-purpose registers are + included, as are special-purpose registers such as the instruction pointer + (program counter). +* Information about each thread of execution within a crashed process, + including: + * The memory region used for each thread’s stack. + * CPU context for each thread, which for various reasons is not the same + as the crash context in the case of the crashed thread. +* A list of loaded code segments (or modules), including: + * The name of the file (`.so`, `.exe`, `.dll`, etc.) which provides the + code. + * The boundaries of the memory region in which the code segment is visible + to the process. + * A reference to the debugging information for the code module, when such + information is available. + +Ordinarily, dumps are produced as a result of a crash, but other triggers may be +set to produce dumps at any time a developer deems appropriate. The Breakpad +processor can handle dumps in the minidump format, either generated by an +[Breakpad client “handler”](client_design.md) implementation, or by another +implementation that produces dumps in this format. The +[DbgHelp.dll!MiniDumpWriteDump] +(http://msdn2.microsoft.com/en-us/library/ms680360.aspx) function on Windows +produces dumps in this format, and is the basis for the Breakpad handler +implementation on that platform. + +The [minidump format] +(http://msdn.microsoft.com/en-us/library/ms679293%28VS.85%29.aspx) is +essentially a simple container format, organized as a series of streams. Each +stream contains some type of data relevant to the crash. A typical “normal” +minidump contains streams for the thread list, the module list, the CPU context +at the time of the crash, and various bits of additional system information. +Other types of minidump can be generated, such as a full-memory minidump, which +in addition to stack memory contains snapshots of all of a process’ mapped +memory regions. + +The minidump format was chosen as Breakpad’s dump format because it has an +established track record on Windows, and it can be adapted to meet the needs of +the other platforms that Breakpad supports. Most other operating systems use +“core” files as their native dump formats, but the capabilities of core files +vary across platforms, and because core files are usually presented in a +platform’s native executable format, there are complications involved in +accessing the data contained therein without the benefit of the header files +that define an executable format’s entire structure. Because minidumps are +leaner than a typical executable format, a redefinition of the format in a +cross-platform header file, `minidump_format.h`, was a straightforward task. +Similarly, the capabilities of the minidump format are understood, and because +it provides an extensible container, any of Breakpad’s needs that could not be +met directly by the standard minidump format could likely be met by extending it +as needed. Finally, using this format means that the dump file is compatible +with native debugging tools at least on Windows. A possible future avenue for +exploration is the conversion of minidumps to core files, to enable this same +benefit on other platforms. + +We have already provided an extension to the minidump format that allows it to +carry dumps generated on systems with PowerPC processors. The format already +allows for variable CPUs, so our work in this area was limited to defining a +context structure sufficient to represent the execution state of a PowerPC. We +have also defined an extension that allows minidumps to indicate which thread of +execution requested a dump be produced for non-crash dumps. + +Often, the information contained within a dump alone is sufficient to produce a +full stack backtrace for each thread. Certain optimizations that compilers +employ in producing code frustrate this process. Specifically, the “frame +pointer omission” optimization of x86 compilers can make it impossible to +produce useful stack traces given only a stack snapshot and CPU context. In +these cases, however, compiler-emitted debugging information can aid in +producing useful stack traces. The Breakpad processor is able to take advantage +of this debugging information as supplied by Microsoft’s C/C++ compiler, the +only compiler to apply such optimizations by default. As a result, the Breakpad +processor can produce useful stack traces even from code with frame pointer +omission optimizations as produced by this compiler. + +### Symbol Files + +The [symbol files](symbol_files.md) that the Breakpad processor accepts allow +for frame pointer omission data, but this is only one of their capabilities. +Each symbol file also includes information about the functions, source files, +and source code line numbers for a single module of code. A module is an +individually-loadble chunk of code: these can be executables containing a main +program (`exe` files on Windows) or shared libraries (`.so` files on Linux, +`.dylib` files, frameworks, and bundles on Mac OS X, and `.dll` files on +Windows). Dumps contain information about which of these modules were loaded at +the time the dump was produced, and given this information, the Breakpad +processor attempts to locate debugging symbols for the module through a +user-supplied function embodied in a “symbol supplier.” Breakpad includes a +sample symbol supplier, called `SimpleSymbolSupplier`, that is used by its +command-line tools; this supplier locates symbol files by pathname. +`SimpleSymbolSupplier` is also available to other users of the Breakpad +processor library. This allows for the use of a simple reference implementation, +but preserves flexibility for users who may have more demanding symbol file +storage needs. + +Breakpad’s symbol file format is text-based, and was defined to be fairly +human-readable and to encompass the needs of multiple platforms. The Breakpad +processor itself does not operate directly with native symbol formats ([DWARF] +(http://dwarf.freestandards.org/) and [STABS] +(http://sourceware.org/gdb/current/onlinedocs/stabs.html) on most Unix-like +systems, [.pdb files] +(http://msdn2.microsoft.com/en-us/library/yd4f8bd1(VS.80).aspx) on Windows), +because of the complications in accessing potentially complex symbol formats +with slight variations between platforms, stored within different types of +binary formats. In the case of `.pdb` files, the debugging format is not even +documented. Instead, Breakpad’s symbol files are produced on each platform, +using specific debugging APIs where available, to convert native symbols to +Breakpad’s cross-platform format. + +### Processing + +Most commonly, a developer will enable an application to use Breakpad by +building it with a platform-specific [client “handler”](client_design.md) +library. After building the application, the developer will create symbol files +for Breakpad’s use using the included `dump_syms` or `symupload` tools, or +another suitable tool, and place the symbol files where the processor’s symbol +supplier will be able to locate them. + +When a dump file is given to the processor’s `MinidumpProcessor` class, it will +read it using its included minidump reader, contained in the `Minidump` family +of classes. It will collect information about the operating system and CPU that +produced the dump, and determine whether the dump was produced as a result of a +crash or at the direct request of the application itself. It then loops over all +of the threads in a process, attempting to walk the stack associated with each +thread. This process is achieved by the processor’s `Stackwalker` components, of +which there are a slightly different implementations for each CPU type that the +processor is able to handle dumps from. Beginning with a thread’s context, and +possibly using debugging data, the stackwalker produces a list of stack frames, +containing each instruction executed in the chain. These instructions are +matched up with the modules that contributed them to a process, and the +`SymbolSupplier` is invoked to locate a symbol file. The symbol file is given to +a `SourceLineResolver`, which matches the instruction up with a specific +function name, source file, and line number, resulting in a representation of a +stack frame that can easily be used to identify which code was executing. + +The results of processing are made available in a `ProcessState` object, which +contains a vector of threads, each containing a vector of stack frames. + +For small-scale use of the Breakpad processor, and for testing and debugging, +the `minidump_stackwalk` tool is provided. It invokes the processor and displays +the full results of processing, optionally allowing symbols to be provided to +the processor by a pathname-based symbol supplier, `SimpleSymbolSupplier`. + +For lower-level testing and debugging, the processor library also includes a +`minidump_dump` tool, which walks through an entire minidump file and displays +its contents in somewhat readable form. + +### Platform Support + +The Breakpad processor library is able to process dumps produced on Mac OS X +systems running on x86, x86-64, and PowerPC processors, on Windows and Linux +systems running on x86 or x86-64 processors, and on Android systems running ARM +or x86 processors. The processor library itself is written in standard C++, and +should function properly in most Unix-like environments. It has been tested on +Linux and Mac OS X. + +## Future Plans + +There are currently no firm plans or timetables to implement any of these +features, although they are possible avenues for future exploration. + +The symbol file format can be extended to carry information about the locations +of parameters and local variables as stored in stack frames and registers, and +the processor can use this information to provide enhanced stack traces showing +function arguments and variable values. + +On Mac OS X and Linux, we can provide tools to convert files from the minidump +format into the native core format. This will enable developers to open dump +files in a native debugger, just as they are presently able to do with minidumps +on Windows. diff --git a/docs/stack_walking.md b/docs/stack_walking.md new file mode 100644 index 00000000..c74f22d4 --- /dev/null +++ b/docs/stack_walking.md @@ -0,0 +1,160 @@ +# 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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc) +class and calling the [MinidumpProcessor::Process] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h) +and a [SourceLineResolverInterface] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#335)), +the list of modules loaded in the process ([MinidumpModuleList] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#501)), +and information about the exception that caused the process to crash +([MinidumpException] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#615)). + +## Enumerating Threads + +For each thread in the thread list ([MinidumpThread] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#299)), +the thread memory containing the stack for the thread ([MinidumpMemoryRegion] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#71) +method. The [BasicSourceLineResolver] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#96) +and [FindCFIFrameInfo] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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] +(http://code.google.com/p/google-breakpad/source/browse/trunk/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. diff --git a/docs/symbol_files.md b/docs/symbol_files.md new file mode 100644 index 00000000..2fc157fc --- /dev/null +++ b/docs/symbol_files.md @@ -0,0 +1,497 @@ +# Introduction + +Given a minidump file, the Breakpad processor produces stack traces that include +function names and source locations. However, minidump files contain only the +byte-by-byte contents of threads' registers and stacks, without function names +or machine-code-to-source mapping data. The processor consults Breakpad symbol +files for the information it needs to produce human-readable stack traces from +the binary-only minidump file. + +The platform-specific symbol dumping tools parse the debugging information the +compiler provides (whether as DWARF or STABS sections in an ELF file or as +stand-alone PDB files), and write that information back out in the Breakpad +symbol file format. This format is much simpler and less detailed than compiler +debugging information, and values legibility over compactness. + +# Overview + +Breakpad symbol files are ASCII text files, with lines delimited as appropriate +for the host platform. Each line is a _record_, divided into fields by single +spaces; in some cases, the last field of the record can contain spaces. The +first field is a string indicating what sort of record the line represents +(except for line records; these are very common, making them the default saves +space). Some fields hold decimal or hexadecimal numbers; hexadecimal numbers +have no "0x" prefix, and use lower-case letters. + +Breakpad symbol files contain the following record types. With some +restrictions, these may appear in any order. + +* A `MODULE` record describes the executable file or shared library from which + this data was derived, for use by symbol suppliers. A `MODULE' record should + be the first record in the file. + +* A `FILE` record gives a source file name, and assigns it a number by which + other records can refer to it. + +* A `FUNC` record describes a function present in the source code. + +* A line record indicates to which source file and line a given range of + machine code should be attributed. The line is attributed to the function + defined by the most recent `FUNC` record. + +* A `PUBLIC` record gives the address of a linker symbol. + +* A `STACK` record provides information necessary to produce stack traces. + +# `MODULE` records + +A `MODULE` record provides meta-information about the module the symbol file +describes. It has the form: + +> `MODULE` _operatingsystem_ _architecture_ _id_ _name_ + +For example: `MODULE Linux x86 D3096ED481217FD4C16B29CD9BC208BA0 firefox-bin +` These records provide meta-information about the executable or shared library +from which this symbol file was generated. A symbol supplier might use this +information to find the correct symbol files to use to interpret a given +minidump, or to perform other sorts of validation. If present, a `MODULE` record +should be the first line in the file. + +The fields are separated by spaces, and cannot contain spaces themselves, except +for _name_. + +* The _operatingsystem_ field names the operating system on which the + executable or shared library was intended to run. This field should have one + of the following values: | **Value** | **Meaning** | + |:----------|:--------------------| | Linux | Linux | | mac | Macintosh OSX + | | windows | Microsoft Windows | + +* The _architecture_ field indicates what processor architecture the + executable or shared library contains machine code for. This field should + have one of the following values: | **Value** | **Instruction Set + Architecture** | |:----------|:---------------------------------| | x86 | + Intel IA-32 | | x86\_64 | AMD64/Intel 64 | | ppc | 32-bit PowerPC | | ppc64 + | 64-bit PowerPC | | unknown | unknown | + +* The _id_ field is a sequence of hexadecimal digits that identifies the exact + executable or library whose contents the symbol file describes. The way in + which it is computed varies from platform to platform. + +* The _name_ field contains the base name (the final component of the + directory path) of the executable or library. It may contain spaces, and + extends to the end of the line. + +# `FILE` records + +A `FILE` record holds a source file name for other records to refer to. It has +the form: + +> `FILE` _number_ _name_ + +For example: `FILE 2 /home/jimb/mc/in/browser/app/nsBrowserApp.cpp +` + +A `FILE` record provides the name of a source file, and assigns it a number +which other records (line records, in particular) can use to refer to that file +name. The _number_ field is a decimal number. The _name_ field is the name of +the file; it may contain spaces. + +# `FUNC` records + +A `FUNC` record describes a source-language function. It has the form: + +> `FUNC` _address_ _size_ _parameter\_size_ _name_ + +For example: `FUNC c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&, +void**) const +` + +The _address_ and _size_ fields are hexadecimal numbers indicating the start +address and length in bytes of the machine code instructions the function +occupies. (Breakpad symbol files cannot accurately describe functions whose code +is not contiguous.) The start address is relative to the module's load address. + +The _parameter\_size_ field is a hexadecimal number indicating the size, in +bytes, of the arguments pushed on the stack for this function. Some calling +conventions, like the Microsoft Windows `stdcall` convention, require the called +function to pop parameters passed to it on the stack from its caller before +returning. The stack walker uses this value, along with data from `STACK` +records, to step from the called function's frame to the caller's frame. + +The _name_ field is the name of the function. In languages that use linker +symbol name mangling like C++, this should be the source language name (the +"unmangled" form). This field may contain spaces. + +# Line records + +A line record describes the source file and line number to which a given range +of machine code should be attributed. It has the form: + +> _address_ _size_ _line_ _filenum_ + +For example: `c184 7 59 4 +` + +Because they are so common, line records do not begin with a string indicating +the record type. All other record types' names use upper-case letters; +hexadecimal numbers, like a line record's _address_, use lower-case letters. + +The _address_ and _size_ fields are hexadecimal numbers indicating the start +address and length in bytes of the machine code. The address is relative to the +module's load address. + +The _line_ field is the line number to which the machine code should be +attributed, in decimal; the first line of the source file is line number 1. The +_filenum_ field is a decimal number appearing in a prior `FILE` record; the name +given in that record is the source file name for the machine code. + +The line is assumed to belong to the function described by the last preceding +`FUNC` record. Line records may not appear before the first `FUNC' record. + +No two line records in a symbol file cover the same range of addresses. However, +there may be many line records with identical line and file numbers, as a given +source line may contribute many non-contiguous blocks of machine code. + +# `PUBLIC` records + +A `PUBLIC` record describes a publicly visible linker symbol, such as that used +to identify an assembly language entry point or region of memory. It has the +form: + +> PUBLIC _address_ _parameter\_size_ _name_ + +For example: `PUBLIC 2160 0 Public2_1 +` + +The Breakpad processor essentially treats a `PUBLIC` record as defining a +function with no line number data and an indeterminate size: the code extends to +the next address mentioned. If a given address is covered by both a `PUBLIC` +record and a `FUNC` record, the processor uses the `FUNC` data. + +The _address_ field is a hexadecimal number indicating the symbol's address, +relative to the module's load address. + +The _parameter\_size_ field is a hexadecimal number indicating the size of the +parameters passed to the code whose entry point the symbol marks, if known. This +field has the same meaning as the _parameter\_size_ field of a `FUNC` record; +see that description for more details. + +The _name_ field is the name of the symbol. In languages that use linker symbol +name mangling like C++, this should be the source language name (the "unmangled" +form). This field may contain spaces. + +# `STACK WIN` records + +Given a stack frame, a `STACK WIN` record indicates how to find the frame that +called it. It has the form: + +> STACK WIN _type_ _rva_ _code\_size_ _prologue\_size_ _epilogue\_size_ +> _parameter\_size_ _saved\_register\_size_ _local\_size_ _max\_stack\_size_ +> _has\_program\_string_ _program\_string\_OR\_allocates\_base\_pointer_ + +For example: `STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = +$ebp $ebp ^ = +` + +All fields of a `STACK WIN` record, except for the last, are hexadecimal +numbers. + +The _type_ field indicates what sort of stack frame data this record holds. Its +value should be one of the values of the [StackFrameTypeEnum] +(http://msdn.microsoft.com/en-us/library/bc5207xw%28VS.100%29.aspx) type in +Microsoft's [Debug Interface Access (DIA)] +(http://msdn.microsoft.com/en-us/library/x93ctkx8%28VS.100%29.aspx) API. +Breakpad uses only records of type 4 (`FrameTypeFrameData`) and 0 +(`FrameTypeFPO`); it ignores others. These types differ only in whether the last +field is an _allocates\_base\_pointer_ flag (`FrameTypeFPO`) or a program string +(`FrameTypeFrameData`). If more than one record covers a given address, Breakpad +prefers `FrameTypeFrameData` records over `FrameTypeFPO` records. + +The _rva_ and _code\_size_ fields give the starting address and length in bytes +of the machine code covered by this record. The starting address is relative to +the module's load address. + +The _prologue\_size_ and _epilogue\_size_ fields give the length, in bytes, of +the prologue and epilogue machine code within the record's range. Breakpad does +not use these values. + +The _parameter\_size_ field gives the number of argument bytes this function +expects to have been passed. This field has the same meaning as the +_parameter\_size_ field of a `FUNC` record; see that description for more +details. + +The _saved\_register\_size_ field gives the number of bytes in the stack frame +dedicated to preserving the values of any callee-saves registers used by this +function. + +The _local\_size_ field gives the number of bytes in the stack frame dedicated +to holding the function's local variables and temporary values. + +The _max\_stack\_size_ field gives the maximum number of bytes pushed on the +stack in the frame. Breakpad does not use this value. + +If the _has\_program\_string_ field is zero, then the `STACK WIN` record's final +field is an _allocates\_base\_pointer_ flag, as a hexadecimal number; this is +expected for records whose _type_ is 0. Otherwise, the final field is a program +string. + +## Interpreting a `STACK WIN` record + +Given the register values for a frame F, we can find the calling frame as +follows: + +* If the _has\_program\_string_ field of a `STACK WIN` record is zero, then + the final field is _allocates\_base\_pointer_, a flag indicating whether the + frame uses the frame pointer register, `%ebp`, as a general-purpose + register. + * If _allocates\_base\_pointer_ is true, then `%ebp` does not point to the + frame's base address. Instead, + * Let _next\_parameter\_size_ be the parameter size of the function + frame F called (**not** this record's _parameter\_size_ field), or + zero if F is the youngest frame on the stack. You must find this + value in F's callee's `FUNC`, `STACK WIN`, or `PUBLIC` records. + * Let _frame\_size_ be the sum of the _local\_size_ field, the + _saved\_register\_size_ field, and _next\_parameter\_size_. > > With + those definitions in place, we can recover the calling frame as + follows: + * F's return address is at `%esp +`_frame\_size_, + * the caller's value of `%ebp` is saved at `%esp + +`_next\_parameter\_size_`+`_saved\_register\_size_`- 8`, and + * the caller's value of `%esp` just before the call instruction was + `%esp +`_frame\_size_`+ 4`. > > (Why do we include + _next\_parameter\_size_ in the sum when computing _frame\_size_ and + the address of the saved `%ebp`? When a function A has called a + function B, the arguments that A pushed for B are considered part of + A's stack frame: A's value for `%esp` points at the last argument + pushed for B. Thus, we must include the size of those arguments + (given by the debugging info for B) along with the size of A's + register save area and local variable area (given by the debugging + info for A) when computing the overall size of A's frame.) + * If _allocates\_base\_pointer_ is false, then F's function doesn't use + `%ebp` at all. You may recover the calling frame as above, except that + the caller's value of `%ebp` is the same as F's value for `%ebp`, so no + steps are necessary to recover it. +* If the _has\_program\_string_ field of a `STACK WIN` record is not zero, + then the record's final field is a string containing a program to be + interpreted to recover the caller's frame. The comments in the + [postfix\_evaluator.h] + (http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/postfix_evaluator.h#40) + header file explain the language in which the program is written. You should + place the following variables in the dictionary before interpreting the + program: + * `$ebp` and `$esp` should be the values of the `%ebp` and `%esp` + registers in F. + * `.cbParams`, `.cbSavedRegs`, and `.cbLocals`, should be the values of + the `STACK WIN` record's _parameter\_size_, _saved\_register\_size_, and + _local\_size_ fields. + * `.raSearchStart` should be set to the address on the stack to begin + scanning for a return address, if necessary. The Breakpad processor sets + this to the value of `%esp` in F, plus the _frame\_size_ value mentioned + above. + +> If the program stores values for `$eip`, `$esp`, `$ebp`, `$ebx`, `$esi`, or +> `$edi`, then those are the values of the given registers in the caller. If the +> value of `$eip` is zero, that indicates that the end of the stack has been +> reached. + +The Breakpad processor checks that the value yielded by the above for the +calling frame's instruction address refers to known code; if the address seems +to be bogus, then it uses a heuristic search to find F's return address and +stack base. + +# `STACK CFI` records + +`STACK CFI` ("Call Frame Information") records describe how to walk the stack +when execution is at a given machine instruction. These records take one of two +forms: + +> `STACK CFI INIT` _address_ _size_ _register1_: +> _expression1_ _register2_: _expression2_ ... +> +> `STACK CFI` _address_ _register1_: _expression1_ +> _register2_: _expression2_ ... + +For example: + +``` +STACK CFI INIT 804c4b0 40 .cfa: $esp 4 + $eip: .cfa 4 - ^ +STACK CFI 804c4b1 .cfa: $esp 8 + $ebp: .cfa 8 - ^ +``` + +The _address_ and _size_ fields are hexadecimal numbers. Each +_register_i is the name of a register or pseudoregister. Each +_expression_ is a Breakpad postfix expression, which may contain spaces, but +never ends with a colon. (The appropriate register names for a given +architecture are determined when `STACK CFI` records are first enabled for that +architecture, and should be documented in the appropriate +`stackwalker_`_architecture_`.cc` source file.) + +STACK CFI records describe, at each machine instruction in a given function, how +to recover the values the machine registers had in the function's caller. +Naturally, some registers' values are simply lost, but there are three cases in +which they can be recovered: + +* You can always recover the program counter, because that's the function's + return address. If the function is ever going to return, the PC must be + saved somewhere. + +* You can always recover the stack pointer. The function is responsible for + popping its stack frame before it returns to the caller, so it must be able + to restore this, as well. + +* You should be able to recover the values of callee-saves registers. These + are registers whose values the callee must preserve, either by saving them + in its own stack frame before using them and re-loading them before + returning, or by not using them at all. + +(As an exception, note that functions which never return may not save any of +this data. It may not be possible to walk the stack past such functions' stack +frames.) + +Given rules for recovering the values of a function's caller's registers, we can +walk up the stack. Starting with the current set of registers --- the PC of the +instruction we're currently executing, the current stack pointer, etc. --- we +use CFI to recover the values those registers had in the caller of the current +frame. This gives us a PC in the caller whose CFI we can look up; we apply the +process again to find that function's caller; and so on. + +Concretely, CFI records represent a table with a row for each machine +instruction address and a column for each register. The table entry for a given +address and register contains a rule describing how, when the PC is at that +address, to restore the value that register had in the caller. + +There are some special columns: + +* A column named `.cfa`, for "Canonical Frame Address", tells how to compute + the base address of the frame; other entries can refer to the CFA in their + rules. + +* A column named `.ra` represents the return address. + +For example, suppose we have a machine with 32-bit registers, one-byte +instructions, a stack that grows downwards, and an assembly language that +resembles C. Suppose further that we have a function whose machine code looks +like this: + +``` +func: ; entry point; return address at sp +func+0: sp -= 16 ; allocate space for stack frame +func+1: sp[12] = r0 ; save 4-byte r0 at sp+12 + ... ; stuff that doesn't affect stack +func+10: sp -= 4; *sp = x ; push some 4-byte x on the stack + ... ; stuff that doesn't affect stack +func+20: r0 = sp[16] ; restore saved r0 +func+21: sp += 20 ; pop whole stack frame +func+22: pc = *sp; sp += 4 ; pop return address and jump to it +``` + +The following table would describe the function above: + +**code address** | **.cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **.ra** +:--------------- | :------- | :---------------------- | :---------------------- | :-- | :------- +func+0 | sp | | | | `cfa[0]` +func+1 | sp+16 | | | | `cfa[0]` +func+2 | sp+16 | `cfa[-4]` | | | `cfa[0]` +func+11 | sp+20 | `cfa[-4]` | | | `cfa[0]` +func+21 | sp+20 | | | | `cfa[0]` +func+22 | sp | | | | `cfa[0]` + +Some things to note here: + +* Each row describes the state of affairs **before** executing the instruction + at the given address. Thus, the row for func+0 describes the state before we + execute the first instruction, which allocates the stack frame. In the next + row, the formula for computing the CFA has changed, reflecting the + allocation. + +* The other entries are written in terms of the CFA; this allows them to + remain unchanged as the stack pointer gets bumped around. For example, to + find the caller's value for r0 (on Google Code) at func+2, we would first + compute the CFA by adding 16 to the sp, and then subtract four from that to + find the address at which r0 (on Google Code) was saved. + +* Although the example doesn't show this, most calling conventions designate + "callee-saves" and "caller-saves" registers. The callee must restore the + values of "callee-saves" registers before returning (if it uses them at + all), whereas the callee is free to use "caller-saves" registers without + restoring their values. A function that uses caller-saves registers + typically does not save their original values at all; in this case, the CFI + marks such registers' values as "unrecoverable". + +* Exactly where the CFA points in the frame --- at the return address? below + it? At some fixed point within the frame? --- is a question of definition + that depends on the architecture and ABI in use. But by definition, the CFA + remains constant throughout the lifetime of the frame. It's up to + architecture- specific code to know what significance to assign the CFA, if + any. + +To save space, the most common type of CFI record only mentions the table +entries at which changes take place. So for the above, the CFI data would only +actually mention the non-blank entries here: + +**insn** | **cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **ra** +:------- | :------ | :---------------------- | :---------------------- | :-- | :------- +func+0 | sp | | | | `cfa[0]` +func+1 | sp+16 | | | | +func+2 | | `cfa[-4]` | | | +func+11 | sp+20 | | | | +func+21 | | r0 (on Google Code) | | | +func+22 | sp | | | | + +A `STACK CFI INIT` record indicates that, at the machine instruction at +_address_, belonging to some function, the value that _registern_ had +in that function's caller can be recovered by evaluating +_expressionn_. The values of any callee-saves registers not mentioned +are assumed to be unchanged. (`STACK CFI` records never mention caller-saves +registers.) These rules apply starting at _address_ and continue up to, but not +including, the address given in the next `STACK CFI` record. The _size_ field is +the total number of bytes of machine code covered by this record and any +subsequent `STACK CFI` records (until the next `STACK CFI INIT` record). The +_address_ field is relative to the module's load address. + +A `STACK CFI` record (no `INIT`) is the same, except that it mentions only those +registers whose recovery rules have changed from the previous CFI record. There +must be a prior `STACK CFI INIT` or `STACK CFI` record in the symbol file. The +_address_ field of this record must be greater than that of the previous record, +and it must not be at or beyond the end of the range given by the most recent +`STACK CFI INIT` record. The address is relative to the module's load address. + +Each expression is a breakpad-style postfix expression. Expressions may contain +spaces, but their tokens may not end with colons. When an expression mentions a +register, it refers to the value of that register in the callee, even if a prior +name/expression pair gives that register's value in the caller. The exception is +`.cfa`, which refers to the canonical frame address computed by the .cfa rule in +force at the current instruction. + +The special expression `.undef` indicates that the given register's value cannot +be recovered. + +The register names preceding the expressions are always followed by colons. The +expressions themselves never contain tokens ending with colons. + +There are two special register names: + +* `.cfa` ("Canonical Frame Address") is the base address of the stack frame. + Other registers' rules may refer to this. If no rule is provided for the + stack pointer, the value of `.cfa` is the caller's stack pointer. + +* `.ra` is the return address. This is the value of the restored program + counter. We use `.ra` instead of the architecture-specific name for the + program counter. + +The Breakpad stack walker requires that there be rules in force for `.cfa` and +`.ra` at every code address from which it unwinds. If those rules are not +present, the stack walker will ignore the `STACK CFI` data, and try to use a +different strategy. + +So the CFI for the example function above would be as follows, if `func` were at +address 0x1000 (relative to the module's load address): + +``` +STACK CFI INIT 1000 .cfa: $sp .ra: .cfa ^ +STACK CFI 1001 .cfa: $sp 16 + +STACK CFI 1002 $r0: .cfa 4 - ^ +STACK CFI 100b .cfa: $sp 20 + +STACK CFI 1015 $r0: $r0 +STACK CFI 1016 .cfa: $sp +``` diff --git a/docs/windows_client_integration.md b/docs/windows_client_integration.md new file mode 100644 index 00000000..99a84926 --- /dev/null +++ b/docs/windows_client_integration.md @@ -0,0 +1,70 @@ +# Windows Integration overview + +## Windows Client Code + +The Windows client code is in the `src/client/windows` directory of the tree. +Since the header files are fairly well commented some specifics are purposely +omitted from this document. + +## Integration of minidump-generation + +Once you build the solution inside `src/client/windows`, an output file of +`exception_handler.lib` will be generated. You can either check this into your +project's directory or build directly from the source, as the project itself +does. + +Enabling Breakpad in your application requires you to `#include +"exception_handler.h"` and instantiate the `ExceptionHandler` object like so: + +``` + handler = new ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info); +``` + +The parameters, in order, are: + +* pathname for minidumps to be written to - this is ignored if OOP dump + generation is used +* A callback that is called when the exception is first handled - you can + return true/false here to continue/stop exception processing +* A callback that is called after minidumps have been written +* Context for the callbacks +* Which exceptions to handle - see `HandlerType` enumeration in + exception\_handler.h +* The type of minidump to generate, using the `MINIDUMP_TYPE` definitions in + `DbgHelp.h` +* A pipe name that can be used to communicate with a crash generation server +* A pointer to a CustomClientInfo class that can be used to send custom data + along with the minidump when using OOP generation + +You can also see `src/client/windows/tests/crash_generation_app/*` for a sample +app that uses OOP generation. + +## OOP Minidump Generation + +For out of process minidump generation, more work is needed. If you look inside +`src/client/windows/crash_generation`, you will see a file called +`crash_generation_server.h`. This file is the interface for a crash generation +server, which must be instantiated with the same pipe name that is passed to the +client above. The logistics of running a separate process that instantiates the +crash generation server is left up to you, however. + +## Build process specifics(symbol generation, upload) + +The symbol creation step is talked about in the general overview doc, since it +doesn't vary much by platform. You'll need to make sure that the symbols are +available wherever minidumps are uploaded to for processing. + +## Out in the field - uploading the minidump + +Inside `src/client/windows/sender` is a class implementation called +`CrashReportSender`. This class can be compiled into a separate standalone CLI +or in the crash generation server and used to upload the report; it can know +when to do so via one of the callbacks provided by the `CrashGenerationServer` +or the `ExceptionHandler` object for in-process generation.