Squashed 'externals/breakpad/' changes from 32e8c7a..999dbf6

999dbf6 directly bring in LSS for simplicity =P
0449204 Get Breakpad Building For Me(tm) against android-10
46fc8fc Allow building of minidump stackwalker on Android
5c786d8 Allow minidump_stackwalk.cc to be embedded
5447d35 Allow printing of stackwalker output to something other than stdio
a78a22d Get Android breakpad Building-For-Me(tm)
a0ac3ee GitHub penance ... ignore libchromeshell.so.sym
c6cade7 Teach dump_syms to handle additional zerofill sections
c341fb0 Fix automake files after README.md -> README rename
69b0606 Add GPU fingerprint information to breakpad microdumps.
ddadd52 Add wiki content to Markdown docs
cba0226 Update configure to look for README.md instead of README
70125ab add markdown docs (converted from Wiki)
bc4ecd1 Linux ExceptionHandler: don't allocate the CrashContext on the stack
REVERT: 32e8c7a Link to lss subtree to unstick local build
REVERT: 21da24f Merge commit '6270714adbd3816049a0fda61da52d7f0789c826' as 'src/third_party/lss-subtree'
REVERT: 6270714 Squashed 'src/third_party/lss-subtree/' content from commit 66889fb
REVERT: 44fdc48 GitHub penance ... ignore libchromeshell.so.sym

git-subtree-dir: externals/breakpad
git-subtree-split: 999dbf65c838d75b73ec0438101ba8690bc4dfae
This commit is contained in:
Aaron Culliney 2015-10-02 21:18:49 -07:00
parent 932e2efeb1
commit 29b5bdf05a
42 changed files with 3353 additions and 203 deletions

View File

@ -76,7 +76,7 @@ dist_doc_DATA = \
INSTALL \
LICENSE \
NEWS \
README
README.md
## Headers
if LINUX_HOST

View File

@ -1883,9 +1883,9 @@ am__DIST_COMMON = $(srcdir)/Makefile.in \
$(top_srcdir)/autotools/missing \
$(top_srcdir)/autotools/test-driver \
$(top_srcdir)/src/config.h.in AUTHORS ChangeLog INSTALL NEWS \
README autotools/compile autotools/config.guess \
autotools/config.sub autotools/depcomp autotools/install-sh \
autotools/ltmain.sh autotools/missing
autotools/compile autotools/config.guess autotools/config.sub \
autotools/depcomp autotools/install-sh autotools/ltmain.sh \
autotools/missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
@ -2028,7 +2028,7 @@ dist_doc_DATA = \
INSTALL \
LICENSE \
NEWS \
README
README.md
@LINUX_HOST_TRUE@includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler
@LINUX_HOST_TRUE@includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h

37
README
View File

@ -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

47
README.md Normal file
View File

@ -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/

View File

@ -59,6 +59,8 @@ LOCAL_MODULE := breakpad_client
LOCAL_CPP_EXTENSION := .cc
LOCAL_CPPFLAGS := -std=gnu++11
# Breakpad uses inline ARM assembly that requires the library
# to be built in ARM mode. Otherwise, the build will fail with
# cryptic assembler messages like:
@ -71,9 +73,12 @@ LOCAL_ARM_MODE := arm
# List of client source files, directly taken from Makefile.am
LOCAL_SRC_FILES := \
src/client/linux/crash_generation/crash_generation_client.cc \
src/client/linux/dump_writer_common/ucontext_reader.cc \
src/client/linux/dump_writer_common/thread_info.cc \
src/client/linux/handler/exception_handler.cc \
src/client/linux/handler/minidump_descriptor.cc \
src/client/linux/log/log.cc \
src/client/linux/microdump_writer/microdump_writer.cc \
src/client/linux/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
@ -89,6 +94,49 @@ LOCAL_SRC_FILES := \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc
# Embedded stackwalker sources (to be able to process crashes in-situ on Android device)
ifeq ($(EMBEDDED_STACKWALKER),1)
LOCAL_CPPFLAGS += -DEMBEDDED_STACKWALKER=1 -frtti
LOCAL_SRC_FILES += \
src/processor/basic_code_modules.cc \
src/processor/basic_source_line_resolver.cc \
src/processor/call_stack.cc \
src/processor/cfi_frame_info.cc \
src/processor/disassembler_x86.cc \
src/processor/dump_context.cc \
src/processor/dump_object.cc \
src/processor/exploitability.cc \
src/processor/exploitability_linux.cc \
src/processor/fast_source_line_resolver.cc \
src/processor/logging.cc \
src/processor/microdump.cc \
src/processor/microdump_processor.cc \
src/processor/microdump_stackwalk.cc \
src/processor/minidump.cc \
src/processor/minidump_dump.cc \
src/processor/minidump_processor.cc \
src/processor/minidump_stackwalk.cc \
src/processor/module_comparer.cc \
src/processor/module_serializer.cc \
src/processor/pathname_stripper.cc \
src/processor/process_state.cc \
src/processor/proc_maps_linux.cc \
src/processor/simple_symbol_supplier.cc \
src/processor/source_line_resolver_base.cc \
src/processor/stack_frame_cpu.cc \
src/processor/stack_frame_symbolizer.cc \
src/processor/stackwalk_common.cc \
src/processor/stackwalker_address_list.cc \
src/processor/stackwalker_amd64.cc \
src/processor/stackwalker_arm64.cc \
src/processor/stackwalker_arm.cc \
src/processor/stackwalker.cc \
src/processor/stackwalker_mips.cc \
src/processor/stackwalker_x86.cc \
src/processor/synth_minidump.cc \
src/processor/tokenize.cc
endif
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \
$(LOCAL_PATH)/src
@ -97,4 +145,4 @@ LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)
# Done.
# Done.

2
configure vendored
View File

@ -585,7 +585,7 @@ PACKAGE_STRING='breakpad 0.1'
PACKAGE_BUGREPORT='google-breakpad-dev@googlegroups.com'
PACKAGE_URL=''
ac_unique_file="README"
ac_unique_file="README.md"
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>

View File

@ -32,7 +32,7 @@ AC_PREREQ(2.57)
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
dnl Sanity check: the argument is just a file that should exist.
AC_CONFIG_SRCDIR(README)
AC_CONFIG_SRCDIR(README.md)
AC_CONFIG_AUX_DIR(autotools)
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST

1
docs/OWNERS Normal file
View File

@ -0,0 +1 @@
*

BIN
docs/breakpad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

1023
docs/breakpad.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 45 KiB

224
docs/client_design.md Normal file
View File

@ -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 programs 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, its 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 its 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 shouldnt 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 systems 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 stacks 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
Breakpads 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
didnt 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 platforms 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 servers 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 platforms 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 weve 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 librarys robustness.
On Windows, we intend to offer tools to make it easier for Breakpads 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
applications launch sequence, including crashes that occur before a programs
main function begins executing.

View File

@ -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 <a
href='http://code.google.com/legal/individual-cla-v1.0.html'>Individual
Contributor License Agreement</a>. 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 <a
href='http://code.google.com/legal/corporate-cla-v1.0.html'> Corporate
Contributor License Agreement</a>. 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.

128
docs/exception_handling.md Normal file
View File

@ -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.

View File

@ -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)
* <a
href='http://code.google.com/p/google-breakpad/wiki/WindowsClientIntegration'>Windows
Integration Guide</a>
* <a
href='http://code.google.com/p/google-breakpad/wiki/MacBreakpadStarterGuide'>Mac
Integration Guide</a>
* <a href='http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide'>
Linux Integration Guide</a>
## 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.

26
docs/how_to_contribute.md Normal file
View File

@ -0,0 +1,26 @@
# How to get an LSS change committed
## Review
You get your change reviewed, you can upload it to
http://codereview.chromium.org (Rietveld) using `git cl upload` from Chromium's
`depot-tools`.
## Testing
Unfortunately, LSS has no automated test suite.
You can test LSS by patching it into Chromium, building Chromium, and running
Chromium's tests. (See [ProjectsUsingLSS](projects_using_lss.md).)
You can compile-test LSS by running:
gcc -Wall -Wextra -Wstrict-prototypes -c linux_syscall_support.h
## Rolling into Chromium
If you commit a change to LSS, please also commit a Chromium change to update
`lss_revision` in Chromium's DEPS file.
This ensures that the LSS change gets tested, so that people who commit later
LSS changes don't run into problems with updating `lss_revision`.

View File

@ -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 <a
href='http://code.google.com/p/google-breakpad/source/browse/trunk/src/client/linux/handler/exception_handler.h'>in
the exception_handler.h source file</a>.
**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 <a
href='http://code.google.com/p/google-breakpad/source/browse/trunk/src/common/linux/linux_libc_support.h'>some
simple reimplementations of libc functions</a>, to avoid calling directly into
libc, as well as <a href='http://code.google.com/p/linux-syscall-support/'>a
header file for making Linux system calls</a> (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 <a
href='http://code.google.com/p/google-breakpad/source/browse/#svn/trunk/src/common/linux'>some
HTTP upload source</a> that you might find useful, as well as <a
href='http://code.google.com/p/google-breakpad/source/browse/#svn/trunk/src/tools/linux/symupload'>a
minidump upload tool</a>.
## Producing symbols for your application
To produce useful stack traces, Breakpad requires you to convert the debugging
symbols in your binaries to <a
href='http://code.google.com/p/google-breakpad/wiki/SymbolFiles'>text-format
symbol files</a>. 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 <a
href='http://mxr.mozilla.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py'>symbolstore.py</a>
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.

View File

@ -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.

View File

@ -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 <location in your
source tree>`
* 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=<location of dump_syms from step 3 above>
"$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 <Breakpad/Breakpad.h>
@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:
`<key>BreakpadProduct</key><string>Google_Notifier_Mac</string>
<key>BreakpadProductDisplay</key><string>${PRODUCT_NAME}</string>
`
## 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:
```
<key>BreakpadURL</key>
<string>upload URL</string>
<key>BreakpadReportInterval</key>
<string>30</string>
```
## 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.

View File

@ -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)

230
docs/processor_design.md Normal file
View File

@ -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 users 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
SupportSofts 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 Microsofts 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 Microsofts
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 threads 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 Breakpads 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
platforms native executable format, there are complications involved in
accessing the data contained therein without the benefit of the header files
that define an executable formats 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 Breakpads 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 Microsofts 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.
Breakpads 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, Breakpads symbol files are produced on each platform,
using specific debugging APIs where available, to convert native symbols to
Breakpads 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 Breakpads use using the included `dump_syms` or `symupload` tools, or
another suitable tool, and place the symbol files where the processors symbol
supplier will be able to locate them.
When a dump file is given to the processors `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 processors `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 threads 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.

View File

@ -0,0 +1,5 @@
# Projects that use linux\_syscall\_support.h
* Chromium
* Breakpad (built as part of Chromium)
* Native Client, in nacl\_bootstrap.c (also built as part of Chromium)

160
docs/stack_walking.md Normal file
View File

@ -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.

497
docs/symbol_files.md Normal file
View File

@ -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_ _register<sub>1</sub>_:
> _expression<sub>1</sub>_ _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
>
> `STACK CFI` _address_ _register<sub>1</sub>_: _expression<sub>1</sub>_
> _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
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_<sub>i</sub> 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 _register<sub>n</sub>_ had
in that function's caller can be recovered by evaluating
_expression<sub>n</sub>_. 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
```

View File

@ -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.

View File

@ -212,6 +212,12 @@ void InstallDefaultHandler(int sig) {
std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack
// for SignalHandler(). Keep the crash context as a .bss field. Exception
// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a
// time can use |g_crash_context_|.
ExceptionHandler::CrashContext g_crash_context_;
} // namespace
// Runs before crashing: normal context.
@ -239,6 +245,11 @@ ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
#endif
pthread_mutex_lock(&g_handler_stack_mutex_);
// Pre-fault the crash context struct. This is to avoid failing due to OOM
// if handling an exception when the process ran out of virtual memory.
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
if (!g_handler_stack_)
g_handler_stack_ = new std::vector<ExceptionHandler*>;
if (install_handler) {
@ -424,36 +435,37 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
}
CrashContext context;
// Fill in all the holes in the struct to make Valgrind happy.
memset(&context, 0, sizeof(context));
memcpy(&context.siginfo, info, sizeof(siginfo_t));
memcpy(&context.context, uc, sizeof(struct ucontext));
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t));
memcpy(&g_crash_context_.context, uc, sizeof(struct ucontext));
#if defined(__aarch64__)
struct ucontext *uc_ptr = (struct ucontext*)uc;
struct fpsimd_context *fp_ptr =
struct ucontext* uc_ptr = (struct ucontext*)uc;
struct fpsimd_context* fp_ptr =
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
memcpy(&context.float_state, fp_ptr, sizeof(context.float_state));
memcpy(&g_crash_context_.float_state, fp_ptr,
sizeof(g_crash_context_.float_state));
}
#elif !defined(__ARM_EABI__) && !defined(__mips__)
#elif !defined(__ARM_EABI__) && !defined(__mips__)
// FP state is not part of user ABI on ARM Linux.
// In case of MIPS Linux FP state is already part of struct ucontext
// and 'float_state' is not a member of CrashContext.
struct ucontext *uc_ptr = (struct ucontext*)uc;
struct ucontext* uc_ptr = (struct ucontext*)uc;
if (uc_ptr->uc_mcontext.fpregs) {
memcpy(&context.float_state,
uc_ptr->uc_mcontext.fpregs,
sizeof(context.float_state));
memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs,
sizeof(g_crash_context_.float_state));
}
#endif
context.tid = syscall(__NR_gettid);
g_crash_context_.tid = syscall(__NR_gettid);
if (crash_handler_ != NULL) {
if (crash_handler_(&context, sizeof(context), callback_context_)) {
if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_),
callback_context_)) {
return true;
}
}
return GenerateDump(&context);
return GenerateDump(&g_crash_context_);
}
// This is a public interface to HandleSignal that allows the client to
@ -580,8 +592,7 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
context,
context_size,
mapping_list_,
minidump_descriptor_.microdump_build_fingerprint(),
minidump_descriptor_.microdump_product_info());
*minidump_descriptor_.microdump_extra_info());
}
if (minidump_descriptor_.IsFD()) {
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),

View File

@ -0,0 +1,48 @@
// Copyright 2015 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
namespace google_breakpad {
struct MicrodumpExtraInfo {
// Strings pointed to by this struct are not copied, and are
// expected to remain valid for the lifetime of the process.
const char* build_fingerprint;
const char* product_info;
const char* gpu_fingerprint;
MicrodumpExtraInfo()
: build_fingerprint(NULL), product_info(NULL), gpu_fingerprint(NULL) {}
};
}
#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_

View File

@ -45,8 +45,7 @@ MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
directory_(descriptor.directory_),
c_path_(NULL),
size_limit_(descriptor.size_limit_),
microdump_build_fingerprint_(descriptor.microdump_build_fingerprint_),
microdump_product_info_(descriptor.microdump_product_info_) {
microdump_extra_info_(descriptor.microdump_extra_info_) {
// The copy constructor is not allowed to be called on a MinidumpDescriptor
// with a valid path_, as getting its c_path_ would require the heap which
// can cause problems in compromised environments.
@ -67,8 +66,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
UpdatePath();
}
size_limit_ = descriptor.size_limit_;
microdump_build_fingerprint_ = descriptor.microdump_build_fingerprint_;
microdump_product_info_ = descriptor.microdump_product_info_;
microdump_extra_info_ = descriptor.microdump_extra_info_;
return *this;
}
@ -86,15 +84,4 @@ void MinidumpDescriptor::UpdatePath() {
c_path_ = path_.c_str();
}
void MinidumpDescriptor::SetMicrodumpBuildFingerprint(
const char* build_fingerprint) {
assert(mode_ == kWriteMicrodumpToConsole);
microdump_build_fingerprint_ = build_fingerprint;
}
void MinidumpDescriptor::SetMicrodumpProductInfo(const char* product_info) {
assert(mode_ == kWriteMicrodumpToConsole);
microdump_product_info_ = product_info;
}
} // namespace google_breakpad

View File

@ -35,6 +35,7 @@
#include <string>
#include "client/linux/handler/microdump_extra_info.h"
#include "common/using_std_string.h"
// This class describes how a crash dump should be generated, either:
@ -49,20 +50,17 @@ class MinidumpDescriptor {
struct MicrodumpOnConsole {};
static const MicrodumpOnConsole kMicrodumpOnConsole;
MinidumpDescriptor() : mode_(kUninitialized),
fd_(-1),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {}
MinidumpDescriptor()
: mode_(kUninitialized),
fd_(-1),
size_limit_(-1) {}
explicit MinidumpDescriptor(const string& directory)
: mode_(kWriteMinidumpToFile),
fd_(-1),
directory_(directory),
c_path_(NULL),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {
size_limit_(-1) {
assert(!directory.empty());
}
@ -70,18 +68,14 @@ class MinidumpDescriptor {
: mode_(kWriteMinidumpToFd),
fd_(fd),
c_path_(NULL),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {
size_limit_(-1) {
assert(fd != -1);
}
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
: mode_(kWriteMicrodumpToConsole),
fd_(-1),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {}
size_limit_(-1) {}
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
@ -107,17 +101,10 @@ class MinidumpDescriptor {
off_t size_limit() const { return size_limit_; }
void set_size_limit(off_t limit) { size_limit_ = limit; }
// TODO(primiano): make this and product info (below) just part of the
// microdump ctor once it is rolled stably into Chrome. ETA: June 2015.
void SetMicrodumpBuildFingerprint(const char* build_fingerprint);
const char* microdump_build_fingerprint() const {
return microdump_build_fingerprint_;
}
void SetMicrodumpProductInfo(const char* product_info);
const char* microdump_product_info() const {
return microdump_product_info_;
}
MicrodumpExtraInfo* microdump_extra_info() {
assert(IsMicrodumpOnConsole());
return &microdump_extra_info_;
};
private:
enum DumpMode {
@ -145,15 +132,16 @@ class MinidumpDescriptor {
off_t size_limit_;
// The product name/version and build fingerprint that should be appended to
// the dump (microdump only). Microdumps don't have the ability of appending
// extra metadata after the dump is generated (as opposite to minidumps
// MIME fields), therefore the product details must be provided upfront.
// The string pointers are supposed to be valid through all the lifetime of
// the process (read: the caller has to guarantee that they are stored in
// global static storage).
const char* microdump_build_fingerprint_;
const char* microdump_product_info_;
// The extra microdump data (e.g. product name/version, build
// fingerprint, gpu fingerprint) that should be appended to the dump
// (microdump only). Microdumps don't have the ability of appending
// extra metadata after the dump is generated (as opposite to
// minidumps MIME fields), therefore the extra data must be provided
// upfront. Any memory pointed to by members of the
// MicrodumpExtraInfo struct must be valid for the lifetime of the
// process (read: the caller has to guarantee that it is stored in
// global static storage.)
MicrodumpExtraInfo microdump_extra_info_;
};
} // namespace google_breakpad

View File

@ -37,6 +37,7 @@
#include "client/linux/dump_writer_common/thread_info.h"
#include "client/linux/dump_writer_common/ucontext_reader.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler/microdump_extra_info.h"
#include "client/linux/log/log.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "common/linux/linux_libc_support.h"
@ -48,6 +49,7 @@ using google_breakpad::LinuxDumper;
using google_breakpad::LinuxPtraceDumper;
using google_breakpad::MappingInfo;
using google_breakpad::MappingList;
using google_breakpad::MicrodumpExtraInfo;
using google_breakpad::RawContextCPU;
using google_breakpad::ThreadInfo;
using google_breakpad::UContextReader;
@ -58,8 +60,7 @@ class MicrodumpWriter {
public:
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info,
const MicrodumpExtraInfo& microdump_extra_info,
LinuxDumper* dumper)
: ucontext_(context ? &context->context : NULL),
#if !defined(__ARM_EABI__) && !defined(__mips__)
@ -67,8 +68,7 @@ class MicrodumpWriter {
#endif
dumper_(dumper),
mapping_list_(mappings),
build_fingerprint_(build_fingerprint),
product_info_(product_info),
microdump_extra_info_(microdump_extra_info),
log_line_(NULL) {
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
if (log_line_)
@ -92,6 +92,7 @@ class MicrodumpWriter {
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
DumpProductInformation();
DumpOSInformation();
DumpGPUInformation();
success = DumpCrashingThread();
if (success)
success = DumpMappings();
@ -149,8 +150,8 @@ class MicrodumpWriter {
void DumpProductInformation() {
LogAppend("V ");
if (product_info_) {
LogAppend(product_info_);
if (microdump_extra_info_.product_info) {
LogAppend(microdump_extra_info_.product_info);
} else {
LogAppend("UNKNOWN:0.0.0.0");
}
@ -200,8 +201,8 @@ class MicrodumpWriter {
// If the client has attached a build fingerprint to the MinidumpDescriptor
// use that one. Otherwise try to get some basic info from uname().
if (build_fingerprint_) {
LogAppend(build_fingerprint_);
if (microdump_extra_info_.build_fingerprint) {
LogAppend(microdump_extra_info_.build_fingerprint);
} else if (has_uts_info) {
LogAppend(uts.release);
LogAppend(" ");
@ -212,6 +213,16 @@ class MicrodumpWriter {
LogCommitLine();
}
void DumpGPUInformation() {
LogAppend("G ");
if (microdump_extra_info_.gpu_fingerprint) {
LogAppend(microdump_extra_info_.gpu_fingerprint);
} else {
LogAppend("UNKNOWN");
}
LogCommitLine();
}
bool DumpThreadStack(uint32_t thread_id,
uintptr_t stack_pointer,
int max_stack_len,
@ -390,8 +401,7 @@ class MicrodumpWriter {
#endif
LinuxDumper* dumper_;
const MappingList& mapping_list_;
const char* const build_fingerprint_;
const char* const product_info_;
const MicrodumpExtraInfo microdump_extra_info_;
char* log_line_;
};
} // namespace
@ -402,8 +412,7 @@ bool WriteMicrodump(pid_t crashing_process,
const void* blob,
size_t blob_size,
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info) {
const MicrodumpExtraInfo& microdump_extra_info) {
LinuxPtraceDumper dumper(crashing_process);
const ExceptionHandler::CrashContext* context = NULL;
if (blob) {
@ -415,8 +424,7 @@ bool WriteMicrodump(pid_t crashing_process,
dumper.set_crash_signal(context->siginfo.si_signo);
dumper.set_crash_thread(context->tid);
}
MicrodumpWriter writer(context, mappings, build_fingerprint, product_info,
&dumper);
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
if (!writer.Init())
return false;
return writer.Dump();

View File

@ -37,6 +37,8 @@
namespace google_breakpad {
struct MicrodumpExtraInfo;
// Writes a microdump (a reduced dump containing only the state of the crashing
// thread) on the console (logcat on Android). These functions do not malloc nor
// use libc functions which may. Thus, it can be used in contexts where the
@ -56,8 +58,7 @@ bool WriteMicrodump(pid_t crashing_process,
const void* blob,
size_t blob_size,
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info);
const MicrodumpExtraInfo& microdump_extra_info);
} // namespace google_breakpad

View File

@ -37,6 +37,7 @@
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler/microdump_extra_info.h"
#include "client/linux/microdump_writer/microdump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/ignore_ret.h"
@ -50,10 +51,20 @@ namespace {
typedef testing::Test MicrodumpWriterTest;
void CrashAndGetMicrodump(
const MappingList& mappings,
MicrodumpExtraInfo MakeMicrodumpExtraInfo(
const char* build_fingerprint,
const char* product_info,
const char* gpu_fingerprint) {
MicrodumpExtraInfo info;
info.build_fingerprint = build_fingerprint;
info.product_info = product_info;
info.gpu_fingerprint = gpu_fingerprint;
return info;
}
void CrashAndGetMicrodump(
const MappingList& mappings,
const MicrodumpExtraInfo& microdump_extra_info,
scoped_array<char>* buf) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
@ -85,7 +96,7 @@ void CrashAndGetMicrodump(
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
build_fingerprint, product_info));
microdump_extra_info));
// Revert stderr back to the console.
dup2(save_err, STDERR_FILENO);
@ -107,12 +118,12 @@ void CrashAndGetMicrodump(
buf->get(), "-----END BREAKPAD MICRODUMP-----"));
}
void CheckMicrodumpContents(const string &microdum_content,
const string &expected_fingerprint,
const string &expected_product_info) {
std::istringstream iss(microdum_content);
void CheckMicrodumpContents(const string& microdump_content,
const MicrodumpExtraInfo& expected_info) {
std::istringstream iss(microdump_content);
bool did_find_os_info = false;
bool did_find_product_info = false;
bool did_find_gpu_info = false;
for (string line; std::getline(iss, line);) {
if (line.find("O ") == 0) {
std::istringstream os_info_tokens(line);
@ -130,15 +141,33 @@ void CheckMicrodumpContents(const string &microdum_content,
// Check that the build fingerprint is in the right place.
os_info_tokens >> token;
ASSERT_EQ(expected_fingerprint, token);
if (expected_info.build_fingerprint)
ASSERT_EQ(expected_info.build_fingerprint, token);
did_find_os_info = true;
} else if (line.find("V ") == 0) {
ASSERT_EQ("V " + expected_product_info, line);
if (expected_info.product_info)
ASSERT_EQ(string("V ") + expected_info.product_info, line);
did_find_product_info = true;
} else if (line.find("G ") == 0) {
if (expected_info.gpu_fingerprint)
ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line);
did_find_gpu_info = true;
}
}
ASSERT_TRUE(did_find_os_info);
ASSERT_TRUE(did_find_product_info);
ASSERT_TRUE(did_find_gpu_info);
}
void CheckMicrodumpContents(const string& microdump_content,
const string& expected_fingerprint,
const string& expected_product_info,
const string& expected_gpu_fingerprint) {
CheckMicrodumpContents(
microdump_content,
MakeMicrodumpExtraInfo(expected_fingerprint.c_str(),
expected_product_info.c_str(),
expected_gpu_fingerprint.c_str()));
}
TEST(MicrodumpWriterTest, BasicWithMappings) {
@ -163,7 +192,7 @@ TEST(MicrodumpWriterTest, BasicWithMappings) {
mappings.push_back(mapping);
scoped_array<char> buf;
CrashAndGetMicrodump(mappings, NULL, NULL, &buf);
CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
#ifdef __LP64__
ASSERT_NE(static_cast<char*>(0), strstr(
@ -187,19 +216,42 @@ TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
const char kProductInfo[] = "MockProduct:42.0.2311.99";
const char kBuildFingerprint[] =
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
const char kGPUFingerprint[] =
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
const MicrodumpExtraInfo kMicrodumpExtraInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
scoped_array<char> buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kBuildFingerprint, kProductInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint, kProductInfo);
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo);
}
TEST(MicrodumpWriterTest, NoProductInfo) {
const char kBuildFingerprint[] = "foobar";
const char kGPUFingerprint[] = "bazqux";
scoped_array<char> buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kBuildFingerprint, NULL, &buf);
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint, "UNKNOWN:0.0.0.0");
const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
"UNKNOWN:0.0.0.0", kGPUFingerprint);
}
TEST(MicrodumpWriterTest, NoGPUInfo) {
const char kProductInfo[] = "bazqux";
const char kBuildFingerprint[] = "foobar";
scoped_array<char> buf;
MappingList no_mappings;
const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
kProductInfo, "UNKNOWN");
}
} // namespace

View File

@ -434,6 +434,9 @@ void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
size_t* dyn_count_ptr) {
uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
#ifndef UINTPTR_MAX
# define UINTPTR_MAX UINT_MAX
#endif
const uintptr_t max_addr = UINTPTR_MAX;
uintptr_t min_vaddr = max_addr;
uintptr_t dyn_vaddr = 0;

View File

@ -481,7 +481,9 @@ bool Reader::WalkSegmentSections(const Segment &segment,
reporter_->SectionsMissing(segment.name);
return false;
}
if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
const uint32_t section_type = section.flags & SECTION_TYPE;
if (section_type == S_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL ||
section_type == S_GB_ZEROFILL) {
// Zero-fill sections have a size, but no contents.
section.contents.start = section.contents.end = NULL;
} else if (segment.contents.start == NULL &&

View File

@ -41,7 +41,9 @@
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/process_state.h"
#include "processor/exploitability_linux.h"
#if !defined(__ANDROID__)
#include "processor/exploitability_win.h"
#endif
#include "processor/logging.h"
namespace google_breakpad {
@ -76,11 +78,13 @@ Exploitability *Exploitability::ExploitabilityForPlatform(
return NULL;
switch (raw_system_info->platform_id) {
#if !defined(__ANDROID__)
case MD_OS_WIN32_NT:
case MD_OS_WIN32_WINDOWS: {
platform_exploitability = new ExploitabilityWin(dump, process_state);
break;
}
#endif
case MD_OS_LINUX: {
platform_exploitability = new ExploitabilityLinux(dump,
process_state,

View File

@ -999,6 +999,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
break;
}
#if !defined(__ANDROID__)
case MD_OS_WIN32_NT:
case MD_OS_WIN32_WINDOWS: {
switch (exception_code) {
@ -1162,6 +1163,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
}
break;
}
#endif
case MD_OS_ANDROID:
case MD_OS_LINUX: {

View File

@ -112,7 +112,24 @@ void usage(const char *program_name) {
} // namespace
#if EMBEDDED_STACKWALKER
extern "C" int stackwalker_main(const char* minidump, const char* symbolsPath, const bool machineReadable) {
int argc = 2;
const char* args[3] = {
"-m",
minidump,
symbolsPath,
};
const char **argv = &args[1];
if (machineReadable) {
++argc;
argv = &args[0];
}
#else
int main(int argc, char **argv) {
#endif
BPLOG_INIT(&argc, &argv);
if (argc < 2) {

View File

@ -52,6 +52,22 @@
#include "processor/logging.h"
#include "processor/pathname_stripper.h"
static FILE* outputFILE = NULL;
static __attribute__((constructor)) void _init_outputFILE(void) {
outputFILE = stdout;
}
#if EMBEDDED_STACKWALKER
extern "C" {
void stackwalker_setOutputFile(const FILE* file) {
outputFILE = (FILE*)file;
if (outputFILE == NULL) {
outputFILE = stdout;
}
}
}
#endif
namespace google_breakpad {
namespace {
@ -75,7 +91,7 @@ static int PrintRegister(const char *name, uint32_t value, int start_col) {
if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
start_col = 0;
printf("\n ");
fprintf(outputFILE, "\n ");
}
fputs(buffer, stdout);
@ -89,7 +105,7 @@ static int PrintRegister64(const char *name, uint64_t value, int start_col) {
if (start_col + static_cast<ssize_t>(strlen(buffer)) > kMaxWidth) {
start_col = 0;
printf("\n ");
fprintf(outputFILE, "\n ");
}
fputs(buffer, stdout);
@ -171,13 +187,13 @@ static void PrintStackContents(const std::string &indent,
return;
// Print stack contents.
printf("\n%sStack contents:", indent.c_str());
fprintf(outputFILE, "\n%sStack contents:", indent.c_str());
for(uint64_t address = stack_begin; address < stack_end; ) {
// Print the start address of this row.
if (word_length == 4)
printf("\n%s %08x", indent.c_str(), static_cast<uint32_t>(address));
fprintf(outputFILE, "\n%s %08x", indent.c_str(), static_cast<uint32_t>(address));
else
printf("\n%s %016" PRIx64, indent.c_str(), address);
fprintf(outputFILE, "\n%s %016" PRIx64, indent.c_str(), address);
// Print data in hex.
const int kBytesPerRow = 16;
@ -186,19 +202,19 @@ static void PrintStackContents(const std::string &indent,
uint8_t value = 0;
if (address < stack_end &&
memory->GetMemoryAtAddress(address, &value)) {
printf(" %02x", value);
fprintf(outputFILE, " %02x", value);
data_as_string.push_back(isprint(value) ? value : '.');
} else {
printf(" ");
fprintf(outputFILE, " ");
data_as_string.push_back(' ');
}
}
// Print data as string.
printf(" %s", data_as_string.c_str());
fprintf(outputFILE, " %s", data_as_string.c_str());
}
// Try to find instruction pointers from stack.
printf("\n%sPossible instruction pointers:\n", indent.c_str());
fprintf(outputFILE, "\n%sPossible instruction pointers:\n", indent.c_str());
for (uint64_t address = stack_begin; address < stack_end;
address += word_length) {
StackFrame pointee_frame;
@ -223,21 +239,21 @@ static void PrintStackContents(const std::string &indent,
// Print function name.
if (!pointee_frame.function_name.empty()) {
if (word_length == 4) {
printf("%s *(0x%08x) = 0x%08x", indent.c_str(),
fprintf(outputFILE, "%s *(0x%08x) = 0x%08x", indent.c_str(),
static_cast<uint32_t>(address),
static_cast<uint32_t>(pointee_frame.instruction));
} else {
printf("%s *(0x%016" PRIx64 ") = 0x%016" PRIx64,
fprintf(outputFILE, "%s *(0x%016" PRIx64 ") = 0x%016" PRIx64,
indent.c_str(), address, pointee_frame.instruction);
}
printf(" <%s> [%s : %d + 0x%" PRIx64 "]\n",
fprintf(outputFILE, " <%s> [%s : %d + 0x%" PRIx64 "]\n",
pointee_frame.function_name.c_str(),
PathnameStripper::File(pointee_frame.source_file_name).c_str(),
pointee_frame.source_line,
pointee_frame.instruction - pointee_frame.source_line_base);
}
}
printf("\n");
fprintf(outputFILE, "\n");
}
// PrintStack prints the call stack in |stack| to stdout, in a reasonably
@ -257,35 +273,35 @@ static void PrintStack(const CallStack *stack,
SourceLineResolverInterface* resolver) {
int frame_count = stack->frames()->size();
if (frame_count == 0) {
printf(" <no frames>\n");
fprintf(outputFILE, " <no frames>\n");
}
for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
const StackFrame *frame = stack->frames()->at(frame_index);
printf("%2d ", frame_index);
fprintf(outputFILE, "%2d ", frame_index);
uint64_t instruction_address = frame->ReturnAddress();
if (frame->module) {
printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
fprintf(outputFILE, "%s", PathnameStripper::File(frame->module->code_file()).c_str());
if (!frame->function_name.empty()) {
printf("!%s", frame->function_name.c_str());
fprintf(outputFILE, "!%s", frame->function_name.c_str());
if (!frame->source_file_name.empty()) {
string source_file = PathnameStripper::File(frame->source_file_name);
printf(" [%s : %d + 0x%" PRIx64 "]",
fprintf(outputFILE, " [%s : %d + 0x%" PRIx64 "]",
source_file.c_str(),
frame->source_line,
instruction_address - frame->source_line_base);
} else {
printf(" + 0x%" PRIx64, instruction_address - frame->function_base);
fprintf(outputFILE, " + 0x%" PRIx64, instruction_address - frame->function_base);
}
} else {
printf(" + 0x%" PRIx64,
fprintf(outputFILE, " + 0x%" PRIx64,
instruction_address - frame->module->base_address());
}
} else {
printf("0x%" PRIx64, instruction_address);
fprintf(outputFILE, "0x%" PRIx64, instruction_address);
}
printf("\n ");
fprintf(outputFILE, "\n ");
int sequence = 0;
if (cpu == "x86") {
@ -603,7 +619,7 @@ static void PrintStack(const CallStack *stack,
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
sequence);
}
printf("\n Found by: %s\n", frame->trust_description().c_str());
fprintf(outputFILE, "\n Found by: %s\n", frame->trust_description().c_str());
// Print stack contents.
if (output_stack_contents && frame_index + 1 < frame_count) {
@ -625,20 +641,20 @@ static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
int frame_count = stack->frames()->size();
for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
const StackFrame *frame = stack->frames()->at(frame_index);
printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index,
fprintf(outputFILE, "%d%c%d%c", thread_num, kOutputSeparator, frame_index,
kOutputSeparator);
uint64_t instruction_address = frame->ReturnAddress();
if (frame->module) {
assert(!frame->module->code_file().empty());
printf("%s", StripSeparator(PathnameStripper::File(
fprintf(outputFILE, "%s", StripSeparator(PathnameStripper::File(
frame->module->code_file())).c_str());
if (!frame->function_name.empty()) {
printf("%c%s", kOutputSeparator,
fprintf(outputFILE, "%c%s", kOutputSeparator,
StripSeparator(frame->function_name).c_str());
if (!frame->source_file_name.empty()) {
printf("%c%s%c%d%c0x%" PRIx64,
fprintf(outputFILE, "%c%s%c%d%c0x%" PRIx64,
kOutputSeparator,
StripSeparator(frame->source_file_name).c_str(),
kOutputSeparator,
@ -646,14 +662,14 @@ static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
kOutputSeparator,
instruction_address - frame->source_line_base);
} else {
printf("%c%c%c0x%" PRIx64,
fprintf(outputFILE, "%c%c%c0x%" PRIx64,
kOutputSeparator, // empty source file
kOutputSeparator, // empty source line
kOutputSeparator,
instruction_address - frame->function_base);
}
} else {
printf("%c%c%c%c0x%" PRIx64,
fprintf(outputFILE, "%c%c%c%c0x%" PRIx64,
kOutputSeparator, // empty function name
kOutputSeparator, // empty source file
kOutputSeparator, // empty source line
@ -662,14 +678,14 @@ static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
}
} else {
// the printf before this prints a trailing separator for module name
printf("%c%c%c%c0x%" PRIx64,
fprintf(outputFILE, "%c%c%c%c0x%" PRIx64,
kOutputSeparator, // empty function name
kOutputSeparator, // empty source file
kOutputSeparator, // empty source line
kOutputSeparator,
instruction_address);
}
printf("\n");
fprintf(outputFILE, "\n");
}
}
@ -709,7 +725,7 @@ static void PrintModule(
module->debug_identifier() + ")";
}
uint64_t base_address = module->base_address();
printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s%s\n",
fprintf(outputFILE, "0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s%s\n",
base_address, base_address + module->size() - 1,
PathnameStripper::File(module->code_file()).c_str(),
module->version().empty() ? "???" : module->version().c_str(),
@ -727,8 +743,8 @@ static void PrintModules(
if (!modules)
return;
printf("\n");
printf("Loaded modules:\n");
fprintf(outputFILE, "\n");
fprintf(outputFILE, "Loaded modules:\n");
uint64_t main_address = 0;
const CodeModule *main_module = modules->GetMainModule();
@ -767,7 +783,7 @@ static void PrintModulesMachineReadable(const CodeModules *modules) {
++module_sequence) {
const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
uint64_t base_address = module->base_address();
printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
fprintf(outputFILE, "Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
kOutputSeparator,
StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
kOutputSeparator, StripSeparator(module->version()).c_str(),
@ -790,30 +806,30 @@ void PrintProcessState(const ProcessState& process_state,
// Print OS and CPU information.
string cpu = process_state.system_info()->cpu;
string cpu_info = process_state.system_info()->cpu_info;
printf("Operating system: %s\n", process_state.system_info()->os.c_str());
printf(" %s\n",
fprintf(outputFILE, "Operating system: %s\n", process_state.system_info()->os.c_str());
fprintf(outputFILE, " %s\n",
process_state.system_info()->os_version.c_str());
printf("CPU: %s\n", cpu.c_str());
fprintf(outputFILE, "CPU: %s\n", cpu.c_str());
if (!cpu_info.empty()) {
// This field is optional.
printf(" %s\n", cpu_info.c_str());
fprintf(outputFILE, " %s\n", cpu_info.c_str());
}
printf(" %d CPU%s\n",
fprintf(outputFILE, " %d CPU%s\n",
process_state.system_info()->cpu_count,
process_state.system_info()->cpu_count != 1 ? "s" : "");
printf("\n");
fprintf(outputFILE, "\n");
// Print crash information.
if (process_state.crashed()) {
printf("Crash reason: %s\n", process_state.crash_reason().c_str());
printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
fprintf(outputFILE, "Crash reason: %s\n", process_state.crash_reason().c_str());
fprintf(outputFILE, "Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
} else {
printf("No crash\n");
fprintf(outputFILE, "No crash\n");
}
string assertion = process_state.assertion();
if (!assertion.empty()) {
printf("Assertion: %s\n", assertion.c_str());
fprintf(outputFILE, "Assertion: %s\n", assertion.c_str());
}
// Compute process uptime if the process creation and crash times are
@ -821,18 +837,18 @@ void PrintProcessState(const ProcessState& process_state,
if (process_state.time_date_stamp() != 0 &&
process_state.process_create_time() != 0 &&
process_state.time_date_stamp() >= process_state.process_create_time()) {
printf("Process uptime: %d seconds\n",
fprintf(outputFILE, "Process uptime: %d seconds\n",
process_state.time_date_stamp() -
process_state.process_create_time());
} else {
printf("Process uptime: not available\n");
fprintf(outputFILE, "Process uptime: not available\n");
}
// If the thread that requested the dump is known, print it first.
int requesting_thread = process_state.requesting_thread();
if (requesting_thread != -1) {
printf("\n");
printf("Thread %d (%s)\n",
fprintf(outputFILE, "\n");
fprintf(outputFILE, "Thread %d (%s)\n",
requesting_thread,
process_state.crashed() ? "crashed" :
"requested dump, did not crash");
@ -847,8 +863,8 @@ void PrintProcessState(const ProcessState& process_state,
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
if (thread_index != requesting_thread) {
// Don't print the crash thread again, it was already printed.
printf("\n");
printf("Thread %d\n", thread_index);
fprintf(outputFILE, "\n");
fprintf(outputFILE, "Thread %d\n", thread_index);
PrintStack(process_state.threads()->at(thread_index), cpu,
output_stack_contents,
process_state.thread_memory_regions()->at(thread_index),
@ -865,11 +881,11 @@ void PrintProcessStateMachineReadable(const ProcessState& process_state) {
// Print OS and CPU information.
// OS|{OS Name}|{OS Version}
// CPU|{CPU Name}|{CPU Info}|{Number of CPUs}
printf("OS%c%s%c%s\n", kOutputSeparator,
fprintf(outputFILE, "OS%c%s%c%s\n", kOutputSeparator,
StripSeparator(process_state.system_info()->os).c_str(),
kOutputSeparator,
StripSeparator(process_state.system_info()->os_version).c_str());
printf("CPU%c%s%c%s%c%d\n", kOutputSeparator,
fprintf(outputFILE, "CPU%c%s%c%s%c%d\n", kOutputSeparator,
StripSeparator(process_state.system_info()->cpu).c_str(),
kOutputSeparator,
// this may be empty
@ -881,9 +897,9 @@ void PrintProcessStateMachineReadable(const ProcessState& process_state) {
// Print crash information.
// Crash|{Crash Reason}|{Crash Address}|{Crashed Thread}
printf("Crash%c", kOutputSeparator);
fprintf(outputFILE, "Crash%c", kOutputSeparator);
if (process_state.crashed()) {
printf("%s%c0x%" PRIx64 "%c",
fprintf(outputFILE, "%s%c0x%" PRIx64 "%c",
StripSeparator(process_state.crash_reason()).c_str(),
kOutputSeparator, process_state.crash_address(), kOutputSeparator);
} else {
@ -891,23 +907,23 @@ void PrintProcessStateMachineReadable(const ProcessState& process_state) {
// instead of the unhelpful "No crash"
string assertion = process_state.assertion();
if (!assertion.empty()) {
printf("%s%c%c", StripSeparator(assertion).c_str(),
fprintf(outputFILE, "%s%c%c", StripSeparator(assertion).c_str(),
kOutputSeparator, kOutputSeparator);
} else {
printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
fprintf(outputFILE, "No crash%c%c", kOutputSeparator, kOutputSeparator);
}
}
if (requesting_thread != -1) {
printf("%d\n", requesting_thread);
fprintf(outputFILE, "%d\n", requesting_thread);
} else {
printf("\n");
fprintf(outputFILE, "\n");
}
PrintModulesMachineReadable(process_state.modules());
// blank line to indicate start of threads
printf("\n");
fprintf(outputFILE, "\n");
// If the thread that requested the dump is known, print it first.
if (requesting_thread != -1) {

View File

@ -34,6 +34,21 @@
#ifndef PROCESSOR_STACKWALK_COMMON_H__
#define PROCESSOR_STACKWALK_COMMON_H__
#if EMBEDDED_STACKWALKER
#include <stdio.h>
extern "C" {
/**
* Sets output to specified FILE*
*/
void stackwalker_setOutputFile(const FILE* file);
/**
* Run stackwalker to process a minidump and symbols
*/
int stackwalker_main(const char* minidump, const char* symbolsPath, const bool machineReadable);
}
#endif
namespace google_breakpad {
class ProcessState;

View File

@ -47,9 +47,11 @@
#include "google_breakpad/processor/system_info.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#if !defined(__ANDROID__)
#include "processor/stackwalker_ppc.h"
#include "processor/stackwalker_ppc64.h"
#include "processor/stackwalker_sparc.h"
#endif
#include "processor/stackwalker_x86.h"
#include "processor/stackwalker_amd64.h"
#include "processor/stackwalker_arm.h"
@ -209,6 +211,7 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
memory, modules, frame_symbolizer);
break;
#if !defined(__ANDROID__)
case MD_CONTEXT_PPC:
cpu_stackwalker = new StackwalkerPPC(system_info,
context->GetContextPPC(),
@ -220,6 +223,7 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
context->GetContextPPC64(),
memory, modules, frame_symbolizer);
break;
#endif
case MD_CONTEXT_AMD64:
cpu_stackwalker = new StackwalkerAMD64(system_info,
@ -227,11 +231,13 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
memory, modules, frame_symbolizer);
break;
#if !defined(__ANDROID__)
case MD_CONTEXT_SPARC:
cpu_stackwalker = new StackwalkerSPARC(system_info,
context->GetContextSPARC(),
memory, modules, frame_symbolizer);
break;
#endif
case MD_CONTEXT_MIPS:
cpu_stackwalker = new StackwalkerMIPS(system_info,

2
src/third_party/lss vendored
View File

@ -1 +1 @@
lss-subtree/lss
lss-stuff/lss