kicad Documentation

This is an active mirror of the KiCad development branch, which is hosted at GitLab (updated every time something is pushed).

3,037 stars C++ C++ 1 file Β· ~60,948 tokens #cad#eda#electronics#engineering#kicad#linux#macos#pcb
RAW Doc

Technical Documentation: KiCad/kicad

ℹ️ Provenance: Multi-Tier Ingestion Β· CodeWiki Reference

Overview (README.md)

KiCad README

For specific documentation about building KiCad, policies
and guidelines, and source code documentation see the
Developer Documentation website.

You may also take a look into the Wiki,
the contribution guide.

For general information about KiCad and information about contributing to the documentation and
libraries, see our Website and our Forum.

Build state

KiCad uses a host of CI resources.

GitLab CI pipeline status can be viewed for Linux and Windows builds of the latest commits.

Release status


Files

  • AUTHORS.txt - The authors, contributors, document writers and translators list
  • CMakeLists.txt - Main CMAKE build tool script
  • copyright.h - A very short copy of the GNU General Public License to be included in new source files
  • Doxyfile - Doxygen config file for KiCad
  • INSTALL.txt - The release (binary) installation instructions
  • uncrustify.cfg - Uncrustify config file for uncrustify sources formatting tool
  • _clang-format - clang config file for clang-format sources formatting tool

Subdirectories

  • 3d-viewer - Sourcecode of the 3D viewer
  • cmake - Modules for the CMAKE build tool
  • common - Sourcecode of the common library
  • cvpcb - Sourcecode of the CvPCB tool
  • demos - Some demo examples
  • doxygen - Configuration for generating pretty doxygen manual of the codebase
  • eeschema - Sourcecode of the schematic editor
  • gerbview - Sourcecode of the gerber viewer
  • include - Interfaces to the common library
  • kicad - Sourcecode of the project manager
  • libs - Sourcecode of KiCad utilities (geometry and others)
  • pagelayout_editor - Sourcecode of the pagelayout editor
  • patches - Collection of patches for external dependencies
  • pcbnew - Sourcecode of the printed circuit board editor
  • plugins - Sourcecode for the 3D viewer plugins
  • qa - Unit testing framework for KiCad
  • resources - Packaging resources such as bitmaps and operating system specific files
  • thirdparty - Sourcecode of external libraries used in KiCad but not written by the KiCad team
  • tools - Helpers for developing, testing and building
  • translation - Translation data files (managed through Weblate for most languages)
  • utils - Small utils for KiCad, e.g. IDF, STEP, and OGL tools and converters

Thirdparty Sentry Native External Benchmark Docs Index (`thirdparty/sentry-native/external/benchmark/docs/index.md`)

Benchmark


Thirdparty Sentry Native External Benchmark Docs AssemblyTests (`thirdparty/sentry-native/external/benchmark/docs/AssemblyTests.md`)

Assembly Tests

The Benchmark library provides a number of functions whose primary
purpose in to affect assembly generation, including DoNotOptimize
and ClobberMemory. In addition there are other functions,
such as KeepRunning, for which generating good assembly is paramount.

For these functions it's important to have tests that verify the
correctness and quality of the implementation. This requires testing
the code generated by the compiler.

This document describes how the Benchmark library tests compiler output,
as well as how to properly write new tests.

Anatomy of a Test

Writing a test has two steps:

  • Write the code you want to generate assembly for.
  • Add // CHECK lines to match against the verified assembly.

Example:

c++
// CHECK-LABEL: test_add:
extern "C" int test_add() {
    extern int ExternInt;
    return ExternInt + 1;

    // CHECK: movl ExternInt(%rip), %eax
    // CHECK: addl %eax
    // CHECK: ret
}

LLVM Filecheck

LLVM's Filecheck
is used to test the generated assembly against the // CHECK lines
specified in the tests source file. Please see the documentation
linked above for information on how to write CHECK directives.

Tips and Tricks:

  • Tests should match the minimal amount of output required to establish
    correctness. CHECK directives don't have to match on the exact next line
    after the previous match, so tests should omit checks for unimportant
    bits of assembly. (CHECK-NEXT
    can be used to ensure a match occurs exactly after the previous match).

  • The tests are compiled with -O3 -g0. So we're only testing the
    optimized output.

  • The assembly output is further cleaned up using tools/strip_asm.py.
    This removes comments, assembler directives, and unused labels before
    the test is run.

  • The generated and stripped assembly file for a test is output under
    /test/<test-name>.s

  • Filecheck supports using CHECK prefixes
    to specify lines that should only match in certain situations.
    The Benchmark tests use CHECK-CLANG and CHECK-GNU for lines that
    are only expected to match Clang or GCC's output respectively. Normal
    CHECK lines match against all compilers. (Note: CHECK-NOT and
    CHECK-LABEL are NOT prefixes. They are versions of non-prefixed
    CHECK lines)

  • Use extern "C" to disable name mangling for specific functions. This
    makes them easier to name in the CHECK lines.

Problems Writing Portable Tests

Writing tests which check the code generated by a compiler are
inherently non-portable. Different compilers and even different compiler
versions may generate entirely different code. The Benchmark tests
must tolerate this.

LLVM Filecheck provides a number of mechanisms to help write
"more portable" tests; including matching using regular expressions,
allowing the creation of named variables
for later matching, and checking non-sequential matches.

Capturing Variables

For example, say GCC stores a variable in a register but Clang stores
it in memory. To write a test that tolerates both cases we "capture"
the destination of the store, and then use the captured expression
to write the remainder of the test.

c++
// CHECK-LABEL: test_div_no_op_into_shr:
extern "C" void test_div_no_op_into_shr(int value) {
    int divisor = 2;
    benchmark::DoNotOptimize(divisor); // hide the value from the optimizer
    return value / divisor;

    // CHECK: movl $2, [[DEST:.*]]
    // CHECK: idivl [[DEST]]
    // CHECK: ret
}

Using Regular Expressions to Match Differing Output

Often tests require testing assembly lines which may subtly differ
between compilers or compiler versions. A common example of this
is matching stack frame addresses. In this case regular expressions
can be used to match the differing bits of output. For example:

c++
int ExternInt;
struct Point { int x, y, z; };

// CHECK-LABEL: test_store_point:
extern "C" void test_store_point() {
    Point p{ExternInt, ExternInt, ExternInt};
    benchmark::DoNotOptimize(p);

    // CHECK: movl ExternInt(%rip), %eax
    // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
    // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
    // CHECK: movl %eax, -{{[0-9]+}}(%rsp)
    // CHECK: ret
}

Current Requirements and Limitations

The tests require Filecheck to be installed along the PATH of the
build machine. Otherwise the tests will be disabled.

Additionally, as mentioned in the previous section, codegen tests are
inherently non-portable. Currently the tests are limited to:

  • x86_64 targets.
  • Compiled with GCC or Clang

Further work could be done, at least on a limited basis, to extend the
tests to other architectures and compilers (using CHECK prefixes).

Furthermore, the tests fail for builds which specify additional flags
that modify code generation, including --coverage or -fsanitize=.


Thirdparty Sentry Native External Benchmark Docs Dependencies (`thirdparty/sentry-native/external/benchmark/docs/dependencies.md`)

Build tool dependency policy

We follow the Foundational C++ support policy for our build tools. In
particular the "Build Systems" section.

CMake

The current supported version is CMake 3.10 as of 2023-08-10. Most modern
distributions include newer versions, for example:

  • Ubuntu 20.04 provides CMake 3.16.3
  • Debian 11.4 provides CMake 3.18.4
  • Ubuntu 22.04 provides CMake 3.22.1

Python

The Python bindings require Python 3.10+ as of v1.9.0 (2024-08-16) for installation from PyPI.
Building from source for older versions probably still works, though. See the user guide for details on how to build from source.
The minimum theoretically supported version is Python 3.8, since the used bindings generator (nanobind) only supports Python 3.8+.


Thirdparty Sentry Native External Benchmark Docs Perf Counters (`thirdparty/sentry-native/external/benchmark/docs/perf_counters.md`)

User-Requested Performance Counters

When running benchmarks, the user may choose to request collection of
performance counters. This may be useful in investigation scenarios - narrowing
down the cause of a regression; or verifying that the underlying cause of a
performance improvement matches expectations.

This feature is available if:

The feature does not require modifying benchmark code. Counter collection is
handled at the boundaries where timer collection is also handled.

To opt-in:

  • If using a Bazel build, add --define pfm=1 to your build flags
  • If using CMake:
    • Install libpfm4-dev, e.g. apt-get install libpfm4-dev.
    • Enable the CMake flag BENCHMARK_ENABLE_LIBPFM in CMakeLists.txt.

To use, pass a comma-separated list of counter names through the
--benchmark_perf_counters flag. The names are decoded through libpfm - meaning,
they are platform specific, but some (e.g. CYCLES or INSTRUCTIONS) are
mapped by libpfm to platform-specifics - see libpfm
documentation for more details.

The counter values are reported back through the User Counters
mechanism, meaning, they are available in all the formats (e.g. JSON) supported
by User Counters.


Thirdparty Sentry Native External Benchmark Docs Platform Specific Build Instructions (`thirdparty/sentry-native/external/benchmark/docs/platform_specific_build_instructions.md`)

Platform Specific Build Instructions

Building with GCC

When the library is built using GCC it is necessary to link with the pthread
library due to how GCC implements std::thread. Failing to link to pthread will
lead to runtime exceptions (unless you're using libc++), not linker errors. See
issue #67 for more details. You
can link to pthread by adding -pthread to your linker command. Note, you can
also use -lpthread, but there are potential issues with ordering of command
line parameters if you use that.

On QNX, the pthread library is part of libc and usually included automatically
(see
pthread_create()).
There's no separate pthread library to link.

Building with Visual Studio 2015 or 2017

The shlwapi library (-lshlwapi) is required to support a call to CPUInfo which reads the registry. Either add shlwapi.lib under [ Configuration Properties > Linker > Input ], or use the following:

text
// Alternatively, can add libraries using linker options.
#ifdef _WIN32
#pragma comment ( lib, "Shlwapi.lib" )
#ifdef _DEBUG
#pragma comment ( lib, "benchmarkd.lib" )
#else
#pragma comment ( lib, "benchmark.lib" )
#endif
#endif

Can also use the graphical version of CMake:

  • Open CMake GUI.
  • Under Where to build the binaries, same path as source plus build.
  • Under CMAKE_INSTALL_PREFIX, same path as source plus install.
  • Click Configure, Generate, Open Project.
  • If build fails, try deleting entire directory and starting again, or unticking options to build less.

Building with Intel 2015 Update 1 or Intel System Studio Update 4

See instructions for building with Visual Studio. Once built, right click on the solution and change the build to Intel.

Building on Solaris

If you're running benchmarks on solaris, you'll want the kstat library linked in
too (-lkstat).


Thirdparty Sentry Native External Benchmark Docs Python Bindings (`thirdparty/sentry-native/external/benchmark/docs/python_bindings.md`)

Building and installing Python bindings

Python bindings are available as wheels on PyPI for importing and
using Google Benchmark directly in Python.
Currently, pre-built wheels exist for macOS (both ARM64 and Intel x86), Linux x86-64 and 64-bit Windows.
Supported Python versions are Python 3.8 - 3.12.

To install Google Benchmark's Python bindings, run:

bash
python -m pip install --upgrade pip  # for manylinux2014 support
python -m pip install google-benchmark

In order to keep your system Python interpreter clean, it is advisable to run these commands in a virtual
environment. See the official Python documentation
on how to create virtual environments.

To build a wheel directly from source, you can follow these steps:

bash
git clone https://github.com/google/benchmark.git
cd benchmark
# create a virtual environment and activate it
python3 -m venv venv --system-site-packages
source venv/bin/activate  # .\venv\Scripts\Activate.ps1 on Windows

# upgrade Python's system-wide packages
python -m pip install --upgrade pip build
# builds the wheel and stores it in the directory "dist".
python -m build

NB: Building wheels from source requires Bazel. For platform-specific instructions on how to install Bazel,
refer to the Bazel installation docs.


Thirdparty Sentry Native External Benchmark Docs Random Interleaving (`thirdparty/sentry-native/external/benchmark/docs/random_interleaving.md`)

Random Interleaving

Random Interleaving is a
technique to lower run-to-run variance. It randomly interleaves repetitions of a
microbenchmark with repetitions from other microbenchmarks in the same benchmark
test. Data shows it is able to lower run-to-run variance by
40% on average.

To use, you mainly need to set --benchmark_enable_random_interleaving=true,
and optionally specify non-zero repetition count --benchmark_repetitions=9
and optionally decrease the per-repetition time --benchmark_min_time=0.1.


Thirdparty Sentry Native External Benchmark Docs Reducing Variance (`thirdparty/sentry-native/external/benchmark/docs/reducing_variance.md`)

Reducing Variance

Disabling CPU Frequency Scaling

If you see this error:

text
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.

you might want to disable the CPU frequency scaling while running the
benchmark, as well as consider other ways to stabilize the performance of
your system while benchmarking.

Exactly how to do this depends on the Linux distribution,
desktop environment, and installed programs. Specific details are a moving
target, so we will not attempt to exhaustively document them here.

One simple option is to use the cpupower program to change the
performance governor to "performance". This tool is maintained along with
the Linux kernel and provided by your distribution.

It must be run as root, like this:

bash
sudo cpupower frequency-set --governor performance

After this you can verify that all CPUs are using the performance governor
by running this command:

bash
cpupower frequency-info -o proc

The benchmarks you subsequently run will have less variance.

Reducing Variance in Benchmarks

The Linux CPU frequency governor discussed
above
is not the only source
of noise in benchmarks. Some, but not all, of the sources of variance
include:

  1. On multi-core machines not all CPUs/CPU cores/CPU threads run the same
    speed, so running a benchmark one time and then again may give a
    different result depending on which CPU it ran on.
  2. CPU scaling features that run on the CPU, like Intel's Turbo Boost and
    AMD Turbo Core and Precision Boost, can temporarily change the CPU
    frequency even when the using the "performance" governor on Linux.
  3. Context switching between CPUs, or scheduling competition on the CPU the
    benchmark is running on.
  4. Intel Hyperthreading or AMD SMT causing the same issue as above.
  5. Cache effects caused by code running on other CPUs.
  6. Non-uniform memory architectures (NUMA).

These can cause variance in benchmarks results within a single run
(--benchmark_repetitions=N) or across multiple runs of the benchmark
program.

Reducing sources of variance is OS and architecture dependent, which is one
reason some companies maintain machines dedicated to performance testing.

Some of the easier and effective ways of reducing variance on a typical
Linux workstation are:

  1. Use the performance governor as discussed
    above
    .
  2. Disable processor boosting by:
    sh
    echo 0 | sudo tee /sys/devices/system/cpu/cpufreq/boost
    See the Linux kernel's
    boost.txt
    for more information.
  3. Set the benchmark program's task affinity to a fixed cpu. For example:
    sh
    taskset -c 0 ./mybenchmark
  4. Disabling Hyperthreading/SMT. This can be done in the Bios or using the
    /sys file system (see the LLVM project's Benchmarking
    tips
    ).
  5. Close other programs that do non-trivial things based on timers, such as
    your web browser, desktop environment, etc.
  6. Reduce the working set of your benchmark to fit within the L1 cache, but
    do be aware that this may lead you to optimize for an unrealistic
    situation.

Further resources on this topic:

  1. The LLVM project's Benchmarking
    tips
    .
  2. The Arch Wiki Cpu frequency
    scaling
    page.

Thirdparty Sentry Native External Benchmark Docs Releasing (`thirdparty/sentry-native/external/benchmark/docs/releasing.md`)

How to release

  • Make sure you're on main and synced to HEAD
  • Ensure the project builds and tests run
    • parallel -j0 exec ::: test/*_test can help ensure everything at least
      passes
  • Prepare release notes
    • git log $(git describe --abbrev=0 --tags)..HEAD gives you the list of
      commits between the last annotated tag and HEAD
    • Pick the most interesting.
  • Create one last commit that updates the version saved in CMakeLists.txt, MODULE.bazel,
    and bindings/python/google_benchmark/__init__.py to the release version you're creating.
    (This version will be used if benchmark is installed from the archive you'll be creating
    in the next step.)
text
# CMakeLists.txt
project (benchmark VERSION 1.9.0 LANGUAGES CXX)
text
# MODULE.bazel
module(name = "com_github_google_benchmark", version="1.9.0")
text
# google_benchmark/__init__.py
__version__ = "1.9.0"
  • Create a release through github's interface
    • Note this will create a lightweight tag.
    • Update this to an annotated tag:
      • git pull --tags
      • git tag -a -f <tag> <tag>
      • git push --force --tags origin
  • Confirm that the "Build and upload Python wheels" action runs to completion
    • Run it manually if it hasn't run.

Thirdparty Sentry Native External Benchmark Docs Tools (`thirdparty/sentry-native/external/benchmark/docs/tools.md`)

Benchmark Tools

compare.py

The compare.py can be used to compare the result of benchmarks.

Dependencies

The utility relies on the scipy package which can be installed using pip:

bash
pip3 install -r requirements.txt

Displaying aggregates only

The switch -a / --display_aggregates_only can be used to control the
displayment of the normal iterations vs the aggregates. When passed, it will
be passthrough to the benchmark binaries to be run, and will be accounted for
in the tool itself; only the aggregates will be displayed, but not normal runs.
It only affects the display, the separate runs will still be used to calculate
the U test.

Modes of operation

There are three modes of operation:

  1. Just compare two benchmarks
    The program is invoked like:
bash
$ compare.py benchmarks <benchmark_baseline> <benchmark_contender> [benchmark options]...

Where and either specify a benchmark executable file, or a JSON output file. The type of the input file is automatically detected. If a benchmark executable is specified then the benchmark is run to obtain the results. Otherwise the results are simply loaded from the output file.

[benchmark options] will be passed to the benchmarks invocations. They can be anything that binary accepts, be it either normal --benchmark_* parameters, or some custom parameters your binary takes.

Example output:

text
/* Detailed source-code truncated for AI context efficiency. */

What it does is for the every benchmark from the first run it looks for the benchmark with exactly the same name in the second run, and then compares the results. If the names differ, the benchmark is omitted from the diff.
As you can note, the values in Time and CPU columns are calculated as (new - old) / |old|.

  1. Compare two different filters of one benchmark
    The program is invoked like:
bash
$ compare.py filters <benchmark> <filter_baseline> <filter_contender> [benchmark options]...

Where `` either specify a benchmark executable file, or a JSON output file. The type of the input file is automatically detected. If a benchmark executable is specified then the benchmark is run to obtain the results. Otherwise the results are simply loaded from the output file.

Where <filter_baseline> and <filter_contender> are the same regex filters that you would pass to the [--benchmark_filter=<regex>] parameter of the benchmark binary.

[benchmark options] will be passed to the benchmarks invocations. They can be anything that binary accepts, be it either normal --benchmark_* parameters, or some custom parameters your binary takes.

Example output:

text
/* Detailed source-code truncated for AI context efficiency. */

As you can see, it applies filter to the benchmarks, both when running the benchmark, and before doing the diff. And to make the diff work, the matches are replaced with some common string. Thus, you can compare two different benchmark families within one benchmark binary.
As you can note, the values in Time and CPU columns are calculated as (new - old) / |old|.

  1. Compare filter one from benchmark one to filter two from benchmark two:
    The program is invoked like:
bash
$ compare.py filters <benchmark_baseline> <filter_baseline> <benchmark_contender> <filter_contender> [benchmark options]...

Where and either specify a benchmark executable file, or a JSON output file. The type of the input file is automatically detected. If a benchmark executable is specified then the benchmark is run to obtain the results. Otherwise the results are simply loaded from the output file.

Where <filter_baseline> and <filter_contender> are the same regex filters that you would pass to the [--benchmark_filter=<regex>] parameter of the benchmark binary.

[benchmark options] will be passed to the benchmarks invocations. They can be anything that binary accepts, be it either normal --benchmark_* parameters, or some custom parameters your binary takes.

Example output:

text
/* Detailed source-code truncated for AI context efficiency. */

This is a mix of the previous two modes, two (potentially different) benchmark binaries are run, and a different filter is applied to each one.
As you can note, the values in Time and CPU columns are calculated as (new - old) / |old|.

Note: Interpreting the output

Performance measurements are an art, and performance comparisons are doubly so.
Results are often noisy and don't necessarily have large absolute differences to
them, so just by visual inspection, it is not at all apparent if two
measurements are actually showing a performance change or not. It is even more
confusing with multiple benchmark repetitions.

Thankfully, what we can do, is use statistical tests on the results to determine
whether the performance has statistically-significantly changed. compare.py
uses Mann–Whitney U
test
, with a null
hypothesis being that there's no difference in performance.

The below output is a summary of a benchmark comparison with statistics
provided for a multi-threaded process.

text
Benchmark                                               Time        CPU    Time Old      Time New       CPU Old       CPU New
-----------------------------------------------------------------------------------------------------------------------------
benchmark/threads:1/process_time/real_time_pvalue     0.0000     0.0000    U Test, Repetitions: 27 vs 27
benchmark/threads:1/process_time/real_time_mean      -0.1442    -0.1442          90            77            90            77
benchmark/threads:1/process_time/real_time_median    -0.1444    -0.1444          90            77            90            77
benchmark/threads:1/process_time/real_time_stddev    +0.3974    +0.3933           0             0             0             0
benchmark/threads:1/process_time/real_time_cv        +0.6329    +0.6280           0             0             0             0
OVERALL_GEOMEAN                                      -0.1442    -0.1442           0             0             0             0

Here's a breakdown of each row:

benchmark/threads:1/process_time/real_time_pvalue: This shows the p-value for
the statistical test comparing the performance of the process running with one
thread. A value of 0.0000 suggests a statistically significant difference in
performance. The comparison was conducted using the U Test (Mann-Whitney
U Test) with 27 repetitions for each case.

benchmark/threads:1/process_time/real_time_mean: This shows the relative
difference in mean execution time between two different cases. The negative
value (-0.1442) implies that the new process is faster by about 14.42%. The old
time was 90 units, while the new time is 77 units.

benchmark/threads:1/process_time/real_time_median: Similarly, this shows the
relative difference in the median execution time. Again, the new process is
faster by 14.44%.

benchmark/threads:1/process_time/real_time_stddev: This is the relative
difference in the standard deviation of the execution time, which is a measure
of how much variation or dispersion there is from the mean. A positive value
(+0.3974) implies there is more variance in the execution time in the new
process.

benchmark/threads:1/process_time/real_time_cv: CV stands for Coefficient of
Variation. It is the ratio of the standard deviation to the mean. It provides a
standardized measure of dispersion. An increase (+0.6329) indicates more
relative variability in the new process.

OVERALL_GEOMEAN: Geomean stands for geometric mean, a type of average that is
less influenced by outliers. The negative value indicates a general improvement
in the new process. However, given the values are all zero for the old and new
times, this seems to be a mistake or placeholder in the output.


Let's first try to see what the different columns represent in the above
compare.py benchmarking output:

  1. Benchmark: The name of the function being benchmarked, along with the
    size of the input (after the slash).

  2. Time: The average time per operation, across all iterations.

  3. CPU: The average CPU time per operation, across all iterations.

  4. Iterations: The number of iterations the benchmark was run to get a
    stable estimate.

  5. Time Old and Time New: These represent the average time it takes for a
    function to run in two different scenarios or versions. For example, you
    might be comparing how fast a function runs before and after you make some
    changes to it.

  6. CPU Old and CPU New: These show the average amount of CPU time that the
    function uses in two different scenarios or versions. This is similar to
    Time Old and Time New, but focuses on CPU usage instead of overall time.

In the comparison section, the relative differences in both time and CPU time
are displayed for each input size.

A statistically-significant difference is determined by a p-value, which is
a measure of the probability that the observed difference could have occurred
just by random chance. A smaller p-value indicates stronger evidence against the
null hypothesis.

Therefore:

  1. If the p-value is less than the chosen significance level (alpha), we
    reject the null hypothesis and conclude the benchmarks are significantly
    different.
  2. If the p-value is greater than or equal to alpha, we fail to reject the
    null hypothesis and treat the two benchmarks as similar.

The result of said the statistical test is additionally communicated through color coding:

diff
+ Green:

The benchmarks are statistically different. This could mean the
performance has either significantly improved or significantly
deteriorated
. You should look at the actual performance numbers to see which
is the case.

diff
- Red:

The benchmarks are statistically similar. This means the performance
hasn't significantly changed.

In statistical terms, 'green' means we reject the null hypothesis that
there's no difference in performance, and 'red' means we fail to reject the
null hypothesis. This might seem counter-intuitive if you're expecting 'green'
to mean 'improved performance' and 'red' to mean 'worsened performance'.

bash
But remember, in this context:

    'Success' means 'successfully finding a difference'.
    'Failure' means 'failing to find a difference'.

Also, please note that even if we determine that there is a
statistically-significant difference between the two measurements, it does not
necessarily mean that the actual benchmarks that were measured are
different, or vice versa, even if we determine that there is no
statistically-significant difference between the two measurements, it does not
necessarily mean that the actual benchmarks that were measured are not
different.

U test

If there is a sufficient repetition count of the benchmarks, the tool can do
a U Test, of the
null hypothesis that it is equally likely that a randomly selected value from
one sample will be less than or greater than a randomly selected value from a
second sample.

If the calculated p-value is below this value is lower than the significance
level alpha, then the result is said to be statistically significant and the
null hypothesis is rejected. Which in other words means that the two benchmarks
aren't identical.

WARNING: requires LARGE (no less than 9) number of repetitions to be
meaningful!


Thirdparty Sentry Native External Benchmark Docs User Guide (`thirdparty/sentry-native/external/benchmark/docs/user_guide.md`)

User Guide

Command Line

Output Formats

Output Files

Running Benchmarks

Running a Subset of Benchmarks

Result Comparison

Extra Context

Library

Runtime and Reporting Considerations

Setup/Teardown

Passing Arguments

Custom Benchmark Name

Calculating Asymptotic Complexity

Templated Benchmarks

Templated Benchmarks that take arguments

Fixtures

Custom Counters

Multithreaded Benchmarks

CPU Timers

Manual Timing

Setting the Time Unit

Random Interleaving

User-Requested Performance Counters

Preventing Optimization

Reporting Statistics

Custom Statistics

Memory Usage

Using RegisterBenchmark

Exiting with an Error

A Faster KeepRunning Loop

Benchmarking Tips

Disabling CPU Frequency Scaling

Reducing Variance in Benchmarks

Output Formats

The library supports multiple output formats. Use the
--benchmark_format=<console|json|csv> flag (or set the
BENCHMARK_FORMAT=<console|json|csv> environment variable) to set
the format type. console is the default format.

The Console format is intended to be a human readable format. By default
the format generates color output. Context is output on stderr and the
tabular data on stdout. Example tabular output looks like:

text
Benchmark                               Time(ns)    CPU(ns) Iterations
----------------------------------------------------------------------
BM_SetInsert/1024/1                        28928      29349      23853  133.097kiB/s   33.2742k items/s
BM_SetInsert/1024/8                        32065      32913      21375  949.487kiB/s   237.372k items/s
BM_SetInsert/1024/10                       33157      33648      21431  1.13369MiB/s   290.225k items/s

The JSON format outputs human readable json split into two top level attributes.
The context attribute contains information about the run in general, including
information about the CPU and the date.
The benchmarks attribute contains a list of every benchmark run. Example json
output looks like:

json
{
  "context": {
    "date": "2015/03/17-18:40:25",
    "num_cpus": 40,
    "mhz_per_cpu": 2801,
    "cpu_scaling_enabled": false,
    "build_type": "debug"
  },
  "benchmarks": [
    {
      "name": "BM_SetInsert/1024/1",
      "iterations": 94877,
      "real_time": 29275,
      "cpu_time": 29836,
      "bytes_per_second": 134066,
      "items_per_second": 33516
    },
    {
      "name": "BM_SetInsert/1024/8",
      "iterations": 21609,
      "real_time": 32317,
      "cpu_time": 32429,
      "bytes_per_second": 986770,
      "items_per_second": 246693
    },
    {
      "name": "BM_SetInsert/1024/10",
      "iterations": 21393,
      "real_time": 32724,
      "cpu_time": 33355,
      "bytes_per_second": 1199226,
      "items_per_second": 299807
    }
  ]
}

The CSV format outputs comma-separated values. The context is output on stderr
and the CSV itself on stdout. Example CSV output looks like:

text
name,iterations,real_time,cpu_time,bytes_per_second,items_per_second,label
"BM_SetInsert/1024/1",65465,17890.7,8407.45,475768,118942,
"BM_SetInsert/1024/8",116606,18810.1,9766.64,3.27646e+06,819115,
"BM_SetInsert/1024/10",106365,17238.4,8421.53,4.74973e+06,1.18743e+06,

Output Files

Write benchmark results to a file with the --benchmark_out=<filename> option
(or set BENCHMARK_OUT). Specify the output format with
--benchmark_out_format={json|console|csv} (or set
BENCHMARK_OUT_FORMAT={json|console|csv}). Note that the 'csv' reporter is
deprecated and the saved .csv file
is not parsable by csv
parsers.

Specifying --benchmark_out does not suppress the console output.

Running Benchmarks

Benchmarks are executed by running the produced binaries. Benchmarks binaries,
by default, accept options that may be specified either through their command
line interface or by setting environment variables before execution. For every
--option_flag=<value> CLI switch, a corresponding environment variable
OPTION_FLAG=<value> exist and is used as default if set (CLI switches always
prevails). A complete list of CLI options is available running benchmarks
with the --help switch.

Dry runs

To confirm that benchmarks can run successfully without needing to wait for
multiple repetitions and iterations, the --benchmark_dry_run flag can be
used. This will run the benchmarks as normal, but for 1 iteration and 1
repetition only.

Running a Subset of Benchmarks

The --benchmark_filter=<regex> option (or BENCHMARK_FILTER=<regex>
environment variable) can be used to only run the benchmarks that match
the specified <regex>. For example:

bash
$ ./run_benchmarks.x --benchmark_filter=BM_memcpy/32
Run on (1 X 2300 MHz CPU )
2016-06-25 19:34:24
Benchmark              Time           CPU Iterations
----------------------------------------------------
BM_memcpy/32          11 ns         11 ns   79545455
BM_memcpy/32k       2181 ns       2185 ns     324074
BM_memcpy/32          12 ns         12 ns   54687500
BM_memcpy/32k       1834 ns       1837 ns     357143

Disabling Benchmarks

It is possible to temporarily disable benchmarks by renaming the benchmark
function to have the prefix "DISABLED_". This will cause the benchmark to
be skipped at runtime.

Result comparison

It is possible to compare the benchmarking results.
See
Additional Tooling Documentation

Extra Context

Sometimes it's useful to add extra context to the content printed before the
results. By default this section includes information about the CPU on which
the benchmarks are running. If you do want to add more context, you can use
the benchmark_context command line flag:

bash
$ ./run_benchmarks --benchmark_context=pwd=`pwd`
Run on (1 x 2300 MHz CPU)
pwd: /home/user/benchmark/
Benchmark              Time           CPU Iterations
----------------------------------------------------
BM_memcpy/32          11 ns         11 ns   79545455
BM_memcpy/32k       2181 ns       2185 ns     324074

You can get the same effect with the API:

c++
benchmark::AddCustomContext("foo", "bar");

Note that attempts to add a second value with the same key will fail with an
error message.

Runtime and Reporting Considerations

When the benchmark binary is executed, each benchmark function is run serially.
The number of iterations to run is determined dynamically by running the
benchmark a few times and measuring the time taken and ensuring that the
ultimate result will be statistically stable. As such, faster benchmark
functions will be run for more iterations than slower benchmark functions, and
the number of iterations is thus reported.

In all cases, the number of iterations for which the benchmark is run is
governed by the amount of time the benchmark takes. Concretely, the number of
iterations is at least one, not more than 1e9, until CPU time is greater than
the minimum time, or the wallclock time is 5x minimum time. The minimum time is
set per benchmark by calling MinTime on the registered benchmark object.

Furthermore warming up a benchmark might be necessary in order to get
stable results because of e.g caching effects of the code under benchmark.
Warming up means running the benchmark a given amount of time, before
results are actually taken into account. The amount of time for which
the warmup should be run can be set per benchmark by calling
MinWarmUpTime on the registered benchmark object or for all benchmarks
using the --benchmark_min_warmup_time command-line option. Note that
MinWarmUpTime will overwrite the value of --benchmark_min_warmup_time
for the single benchmark. How many iterations the warmup run of each
benchmark takes is determined the same way as described in the paragraph
above. Per default the warmup phase is set to 0 seconds and is therefore
disabled.

Average timings are then reported over the iterations run. If multiple
repetitions are requested using the --benchmark_repetitions command-line
option, or at registration time, the benchmark function will be run several
times and statistical results across these repetitions will also be reported.

As well as the per-benchmark entries, a preamble in the report will include
information about the machine on which the benchmarks are run.

Setup/Teardown

Global setup/teardown specific to each benchmark can be done by
passing a callback to Setup/Teardown:

The setup/teardown callbacks will be invoked once for each benchmark. If the
benchmark is multi-threaded (will run in k threads), they will be invoked
exactly once before each run with k threads.

If the benchmark uses different size groups of threads, the above will be true
for each size group.

Eg.,

c++
static void DoSetup(const benchmark::State& state) {
}

static void DoTeardown(const benchmark::State& state) {
}

static void BM_func(benchmark::State& state) {...}

BENCHMARK(BM_func)->Arg(1)->Arg(3)->Threads(16)->Threads(32)->Setup(DoSetup)->Teardown(DoTeardown);

In this example, DoSetup and DoTearDown will be invoked 4 times each,
specifically, once for each of this family:

  • BM_func_Arg_1_Threads_16, BM_func_Arg_1_Threads_32
  • BM_func_Arg_3_Threads_16, BM_func_Arg_3_Threads_32

Passing Arguments

Sometimes a family of benchmarks can be implemented with just one routine that
takes an extra argument to specify which one of the family of benchmarks to
run. For example, the following code defines a family of benchmarks for
measuring the speed of memcpy() calls of different lengths:

c++
static void BM_memcpy(benchmark::State& state) {
  char* src = new char[state.range(0)];
  char* dst = new char[state.range(0)];
  memset(src, 'x', state.range(0));
  for (auto _ : state)
    memcpy(dst, src, state.range(0));
  state.SetBytesProcessed(int64_t(state.iterations()) *
                          int64_t(state.range(0)));
  delete[] src;
  delete[] dst;
}
BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(4<<10)->Arg(8<<10);

The preceding code is quite repetitive, and can be replaced with the following
short-hand. The following invocation will pick a few appropriate arguments in
the specified range and will generate a benchmark for each such argument.

c++
BENCHMARK(BM_memcpy)->Range(8, 8<<10);

By default the arguments in the range are generated in multiples of eight and
the command above selects [ 8, 64, 512, 4k, 8k ]. In the following code the
range multiplier is changed to multiples of two.

c++
BENCHMARK(BM_memcpy)->RangeMultiplier(2)->Range(8, 8<<10);

Now arguments generated are [ 8, 16, 32, 64, 128, 256, 512, 1024, 2k, 4k, 8k ].

The preceding code shows a method of defining a sparse range. The following
example shows a method of defining a dense range. It is then used to benchmark
the performance of std::vector initialization for uniformly increasing sizes.

c++
static void BM_DenseRange(benchmark::State& state) {
  for(auto _ : state) {
    std::vector<int> v(state.range(0), state.range(0));
    auto data = v.data();
    benchmark::DoNotOptimize(data);
    benchmark::ClobberMemory();
  }
}
BENCHMARK(BM_DenseRange)->DenseRange(0, 1024, 128);

Now arguments generated are [ 0, 128, 256, 384, 512, 640, 768, 896, 1024 ].

You might have a benchmark that depends on two or more inputs. For example, the
following code defines a family of benchmarks for measuring the speed of set
insertion.

c++
static void BM_SetInsert(benchmark::State& state) {
  std::set<int> data;
  for (auto _ : state) {
    state.PauseTiming();
    data = ConstructRandomSet(state.range(0));
    state.ResumeTiming();
    for (int j = 0; j < state.range(1); ++j)
      data.insert(RandomNumber());
  }
}
BENCHMARK(BM_SetInsert)
    ->Args({1<<10, 128})
    ->Args({2<<10, 128})
    ->Args({4<<10, 128})
    ->Args({8<<10, 128})
    ->Args({1<<10, 512})
    ->Args({2<<10, 512})
    ->Args({4<<10, 512})
    ->Args({8<<10, 512});

The preceding code is quite repetitive, and can be replaced with the following
short-hand. The following macro will pick a few appropriate arguments in the
product of the two specified ranges and will generate a benchmark for each such
pair.

c++
BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {128, 512}});

Some benchmarks may require specific argument values that cannot be expressed
with Ranges. In this case, ArgsProduct offers the ability to generate a
benchmark input for each combination in the product of the supplied vectors.

c++
BENCHMARK(BM_SetInsert)
    ->ArgsProduct({{1<<10, 3<<10, 8<<10}, {20, 40, 60, 80}})
// would generate the same benchmark arguments as
BENCHMARK(BM_SetInsert)
    ->Args({1<<10, 20})
    ->Args({3<<10, 20})
    ->Args({8<<10, 20})
    ->Args({3<<10, 40})
    ->Args({8<<10, 40})
    ->Args({1<<10, 40})
    ->Args({1<<10, 60})
    ->Args({3<<10, 60})
    ->Args({8<<10, 60})
    ->Args({1<<10, 80})
    ->Args({3<<10, 80})
    ->Args({8<<10, 80});

For the most common scenarios, helper methods for creating a list of
integers for a given sparse or dense range are provided.

c++
BENCHMARK(BM_SetInsert)
    ->ArgsProduct({
      benchmark::CreateRange(8, 128, /*multi=*/2),
      benchmark::CreateDenseRange(1, 4, /*step=*/1)
    })
// would generate the same benchmark arguments as
BENCHMARK(BM_SetInsert)
    ->ArgsProduct({
      {8, 16, 32, 64, 128},
      {1, 2, 3, 4}
    });

For more complex patterns of inputs, passing a custom function to Apply allows
programmatic specification of an arbitrary set of arguments on which to run the
benchmark. The following example enumerates a dense range on one parameter,
and a sparse range on the second.

c++
static void CustomArguments(benchmark::internal::Benchmark* b) {
  for (int i = 0; i <= 10; ++i)
    for (int j = 32; j <= 1024*1024; j *= 8)
      b->Args({i, j});
}
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);

Passing Arbitrary Arguments to a Benchmark

It is possible to define a benchmark that takes an arbitrary number
of extra arguments. The BENCHMARK_CAPTURE(func, test_case_name, ...args)
macro creates a benchmark that invokes func with the benchmark::State as
the first argument followed by the specified args....
The test_case_name is appended to the name of the benchmark and
should describe the values passed.

c++
template <class ...Args>
void BM_takes_args(benchmark::State& state, Args&&... args) {
  auto args_tuple = std::make_tuple(std::move(args)...);
  for (auto _ : state) {
    std::cout << std::get<0>(args_tuple) << ": " << std::get<1>(args_tuple)
              << '\n';
    [...]
  }
}
// Registers a benchmark named "BM_takes_args/int_string_test" that passes
// the specified values to `args`.
BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc"));

// Registers the same benchmark "BM_takes_args/int_test" that passes
// the specified values to `args`.
BENCHMARK_CAPTURE(BM_takes_args, int_test, 42, 43);

Note that elements of ...args may refer to global variables. Users should
avoid modifying global state inside of a benchmark.

Calculating Asymptotic Complexity (Big O)

Asymptotic complexity might be calculated for a family of benchmarks. The
following code will calculate the coefficient for the high-order term in the
running time and the normalized root-mean square error of string comparison.

c++
static void BM_StringCompare(benchmark::State& state) {
  std::string s1(state.range(0), '-');
  std::string s2(state.range(0), '-');
  for (auto _ : state) {
    auto comparison_result = s1.compare(s2);
    benchmark::DoNotOptimize(comparison_result);
  }
  state.SetComplexityN(state.range(0));
}
BENCHMARK(BM_StringCompare)
    ->RangeMultiplier(2)->Range(1<<10, 1<<18)->Complexity(benchmark::oN);

As shown in the following invocation, asymptotic complexity might also be
calculated automatically.

c++
BENCHMARK(BM_StringCompare)
    ->RangeMultiplier(2)->Range(1<<10, 1<<18)->Complexity();

The following code will specify asymptotic complexity with a lambda function,
that might be used to customize high-order term calculation.

c++
BENCHMARK(BM_StringCompare)->RangeMultiplier(2)
    ->Range(1<<10, 1<<18)->Complexity([](benchmark::IterationCount n)->double{return n; });

Custom Benchmark Name

You can change the benchmark's name as follows:

c++
BENCHMARK(BM_memcpy)->Name("memcpy")->RangeMultiplier(2)->Range(8, 8<<10);

The invocation will execute the benchmark as before using BM_memcpy but changes
the prefix in the report to memcpy.

Templated Benchmarks

This example produces and consumes messages of size sizeof(v) range_x
times. It also outputs throughput in the absence of multiprogramming.

c++
template <class Q> void BM_Sequential(benchmark::State& state) {
  Q q;
  typename Q::value_type v;
  for (auto _ : state) {
    for (int i = state.range(0); i--; )
      q.push(v);
    for (int e = state.range(0); e--; )
      q.Wait(&v);
  }
  // actually messages, not bytes:
  state.SetBytesProcessed(
      static_cast<int64_t>(state.iterations())*state.range(0));
}

// You can use the BENCHMARK macro with template parameters:
BENCHMARK(BM_Sequential<WaitQueue<int>>)->Range(1<<0, 1<<10);

// Old, legacy verbose C++03 syntax:
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);

Three macros are provided for adding benchmark templates.

c++
#define BENCHMARK(func<...>) // Takes any number of parameters.
#define BENCHMARK_TEMPLATE1(func, arg1)
#define BENCHMARK_TEMPLATE2(func, arg1, arg2)

Templated Benchmarks that take arguments

Sometimes there is a need to template benchmarks, and provide arguments to them.

c++
template <class Q> void BM_Sequential_With_Step(benchmark::State& state, int step) {
  Q q;
  typename Q::value_type v;
  for (auto _ : state) {
    for (int i = state.range(0); i-=step; )
      q.push(v);
    for (int e = state.range(0); e-=step; )
      q.Wait(&v);
  }
  // actually messages, not bytes:
  state.SetBytesProcessed(
      static_cast<int64_t>(state.iterations())*state.range(0));
}

BENCHMARK_TEMPLATE1_CAPTURE(BM_Sequential, WaitQueue<int>, Step1, 1)->Range(1<<0, 1<<10);

Fixtures

Fixture tests are created by first defining a type that derives from
::benchmark::Fixture and then creating/registering the tests using the
following macros:

  • BENCHMARK_F(ClassName, Method)
  • BENCHMARK_DEFINE_F(ClassName, Method)
  • BENCHMARK_REGISTER_F(ClassName, Method)

For Example:

c++
class MyFixture : public benchmark::Fixture {
public:
  void SetUp(::benchmark::State& state) {
  }

  void TearDown(::benchmark::State& state) {
  }
};

// Defines and registers `FooTest` using the class `MyFixture`.
BENCHMARK_F(MyFixture, FooTest)(benchmark::State& st) {
   for (auto _ : st) {
     ...
  }
}

// Only defines `BarTest` using the class `MyFixture`.
BENCHMARK_DEFINE_F(MyFixture, BarTest)(benchmark::State& st) {
   for (auto _ : st) {
     ...
  }
}
// `BarTest` is NOT registered.
BENCHMARK_REGISTER_F(MyFixture, BarTest)->Threads(2);
// `BarTest` is now registered.

Templated Fixtures

Also you can create templated fixture by using the following macros:

  • BENCHMARK_TEMPLATE_F(ClassName, Method, ...)
  • BENCHMARK_TEMPLATE_DEFINE_F(ClassName, Method, ...)

For example:

c++
template<typename T>
class MyFixture : public benchmark::Fixture {};

// Defines and registers `IntTest` using the class template `MyFixture<int>`.
BENCHMARK_TEMPLATE_F(MyFixture, IntTest, int)(benchmark::State& st) {
   for (auto _ : st) {
     ...
  }
}

// Only defines `DoubleTest` using the class template `MyFixture<double>`.
BENCHMARK_TEMPLATE_DEFINE_F(MyFixture, DoubleTest, double)(benchmark::State& st) {
   for (auto _ : st) {
     ...
  }
}
// `DoubleTest` is NOT registered.
BENCHMARK_REGISTER_F(MyFixture, DoubleTest)->Threads(2);
// `DoubleTest` is now registered.

If you want to use a method template for your fixtures,
which you instantiate afterward, use the following macros:

  • BENCHMARK_TEMPLATE_METHOD_F(ClassName, Method)
  • BENCHMARK_TEMPLATE_INSTANTIATE_F(ClassName, Method, ...)

With these macros you can define one method for several instantiations.
Example (using MyFixture from above):

c++
// Defines `Test` using the class template `MyFixture`.
BENCHMARK_TEMPLATE_METHOD_F(MyFixture, Test)(benchmark::State& st) {
   for (auto _ : st) {
     ...
  }
}

// Instantiates and registers the benchmark `MyFixture<int>::Test`.
BENCHMARK_TEMPLATE_INSTANTIATE_F(MyFixture, Test, int)->Threads(2);
// Instantiates and registers the benchmark `MyFixture<double>::Test`.
BENCHMARK_TEMPLATE_INSTANTIATE_F(MyFixture, Test, double)->Threads(4);

Inside the method definition of BENCHMARK_TEMPLATE_METHOD_F the type Base refers
to the type of the instantiated fixture.
Accesses to members of the fixture must be prefixed by this->.

BENCHMARK_TEMPLATE_METHOD_Fand BENCHMARK_TEMPLATE_INSTANTIATE_F can only be used,
if the fixture does not use non-type template parameters.
If you want to pass values as template parameters, use e.g. std::integral_constant.
For example:

c++
template<typename Sz>
class SizedFixture : public benchmark::Fixture {
  static constexpr auto Size = Sz::value;
  int myValue;
};

BENCHMARK_TEMPLATE_METHOD_F(SizedFixture, Test)(benchmark::State& st) {
   for (auto _ : st) {
     this->myValue = Base::Size;
  }
}

BENCHMARK_TEMPLATE_INSTANTIATE_F(SizedFixture, Test, std::integral_constant<5>)->Threads(2);

Custom Counters

You can add your own counters with user-defined names. The example below
will add columns "Foo", "Bar" and "Baz" in its output:

c++
static void UserCountersExample1(benchmark::State& state) {
  double numFoos = 0, numBars = 0, numBazs = 0;
  for (auto _ : state) {
    // ... count Foo,Bar,Baz events
  }
  state.counters["Foo"] = numFoos;
  state.counters["Bar"] = numBars;
  state.counters["Baz"] = numBazs;
}

The state.counters object is a std::map with std::string keys
and Counter values. The latter is a double-like class, via an implicit
conversion to double&. Thus you can use all of the standard arithmetic
assignment operators (=,+=,-=,*=,/=) to change the value of each counter.

In multithreaded benchmarks, each counter is set on the calling thread only.
When the benchmark finishes, the counters from each thread will be summed;
the resulting sum is the value which will be shown for the benchmark.

The Counter constructor accepts three parameters: the value as a double
; a bit flag which allows you to show counters as rates, and/or as per-thread
iteration, and/or as per-thread averages, and/or iteration invariants,
and/or finally inverting the result; and a flag specifying the 'unit' - i.e.
is 1k a 1000 (default, benchmark::Counter::OneK::kIs1000), or 1024
(benchmark::Counter::OneK::kIs1024)?

c++
// sets a simple counter
  state.counters["Foo"] = numFoos;

  // Set the counter as a rate. It will be presented divided
  // by the duration of the benchmark.
  // Meaning: per one second, how many 'foo's are processed?
  state.counters["FooRate"] = Counter(numFoos, benchmark::Counter::kIsRate);

  // Set the counter as a rate. It will be presented divided
  // by the duration of the benchmark, and the result inverted.
  // Meaning: how many seconds it takes to process one 'foo'?
  state.counters["FooInvRate"] = Counter(numFoos, benchmark::Counter::kIsRate | benchmark::Counter::kInvert);

  // Set the counter as a thread-average quantity. It will
  // be presented divided by the number of threads.
  state.counters["FooAvg"] = Counter(numFoos, benchmark::Counter::kAvgThreads);

  // There's also a combined flag:
  state.counters["FooAvgRate"] = Counter(numFoos,benchmark::Counter::kAvgThreadsRate);

  // This says that we process with the rate of state.range(0) bytes every iteration:
  state.counters["BytesProcessed"] = Counter(state.range(0), benchmark::Counter::kIsIterationInvariantRate, benchmark::Counter::OneK::kIs1024);

You can use insert() with std::initializer_list:

c++
state.counters.insert({{"Foo", numFoos}, {"Bar", numBars}, {"Baz", numBazs}});
  // ... instead of:
  state.counters["Foo"] = numFoos;
  state.counters["Bar"] = numBars;
  state.counters["Baz"] = numBazs;

Counter Reporting

When using the console reporter, by default, user counters are printed at
the end after the table, the same way as bytes_processed and
items_processed. This is best for cases in which there are few counters,
or where there are only a couple of lines per benchmark. Here's an example of
the default output:

text
------------------------------------------------------------------------------
Benchmark                        Time           CPU Iterations UserCounters...
------------------------------------------------------------------------------
BM_UserCounter/threads:8      2248 ns      10277 ns      68808 Bar=16 Bat=40 Baz=24 Foo=8
BM_UserCounter/threads:1      9797 ns       9788 ns      71523 Bar=2 Bat=5 Baz=3 Foo=1024m
BM_UserCounter/threads:2      4924 ns       9842 ns      71036 Bar=4 Bat=10 Baz=6 Foo=2
BM_UserCounter/threads:4      2589 ns      10284 ns      68012 Bar=8 Bat=20 Baz=12 Foo=4
BM_UserCounter/threads:8      2212 ns      10287 ns      68040 Bar=16 Bat=40 Baz=24 Foo=8
BM_UserCounter/threads:16     1782 ns      10278 ns      68144 Bar=32 Bat=80 Baz=48 Foo=16
BM_UserCounter/threads:32     1291 ns      10296 ns      68256 Bar=64 Bat=160 Baz=96 Foo=32
BM_UserCounter/threads:4      2615 ns      10307 ns      68040 Bar=8 Bat=20 Baz=12 Foo=4
BM_Factorial                    26 ns         26 ns   26608979 40320
BM_Factorial/real_time          26 ns         26 ns   26587936 40320
BM_CalculatePiRange/1           16 ns         16 ns   45704255 0
BM_CalculatePiRange/8           73 ns         73 ns    9520927 3.28374
BM_CalculatePiRange/64         609 ns        609 ns    1140647 3.15746
BM_CalculatePiRange/512       4900 ns       4901 ns     142696 3.14355

If this doesn't suit you, you can print each counter as a table column by
passing the flag --benchmark_counters_tabular=true to the benchmark
application. This is best for cases in which there are a lot of counters, or
a lot of lines per individual benchmark. Note that this will trigger a
reprinting of the table header any time the counter set changes between
individual benchmarks. Here's an example of corresponding output when
--benchmark_counters_tabular=true is passed:

text
/* Detailed source-code truncated for AI context efficiency. */

Note above the additional header printed when the benchmark changes from
BM_UserCounter to BM_Factorial. This is because BM_Factorial does
not have the same counter set as BM_UserCounter.

Multithreaded Benchmarks

In a multithreaded test (benchmark invoked by multiple threads simultaneously),
it is guaranteed that none of the threads will start until all have reached
the start of the benchmark loop, and all will have finished before any thread
exits the benchmark loop. (This behavior is also provided by the KeepRunning()
API) As such, any global setup or teardown can be wrapped in a check against the thread
index:

c++
static void BM_MultiThreaded(benchmark::State& state) {
  if (state.thread_index() == 0) {
    // Setup code here.
  }
  for (auto _ : state) {
    // Run the test as normal.
  }
  if (state.thread_index() == 0) {
    // Teardown code here.
  }
}
BENCHMARK(BM_MultiThreaded)->Threads(2);

To run the benchmark across a range of thread counts, instead of Threads, use
ThreadRange. This takes two parameters (min_threads and max_threads) and
runs the benchmark once for values in the inclusive range. For example:

c++
BENCHMARK(BM_MultiThreaded)->ThreadRange(1, 8);

will run BM_MultiThreaded with thread counts 1, 2, 4, and 8.

If the benchmarked code itself uses threads and you want to compare it to
single-threaded code, you may want to use real-time ("wallclock") measurements
for latency comparisons:

c++
BENCHMARK(BM_test)->Range(8, 8<<10)->UseRealTime();

Without UseRealTime, CPU time is used by default.

Manual Multithreaded Benchmarks

Google/benchmark uses std::thread as multithreading environment per default.
If you want to use another multithreading environment (e.g. OpenMP), you can provide
a factory function to your benchmark using the ThreadRunner function.
The factory function takes the number of threads as argument and creates a custom class
derived from benchmark::ThreadRunnerBase.
This custom class must override the function
void RunThreads(const std::function<void(int)>& fn).
RunThreads is called by the main thread and spawns the requested number of threads.
Each spawned thread must call fn(thread_index), where thread_index is its own
thread index. Before RunThreads returns, all spawned threads must be joined.

c++
class OpenMPThreadRunner : public benchmark::ThreadRunnerBase
{
  OpenMPThreadRunner(int num_threads)
  : num_threads_(num_threads)
  {}

  void RunThreads(const std::function<void(int)>& fn) final
  {
#pragma omp parallel num_threads(num_threads_)
    fn(omp_get_thread_num());
  }

private:
  int num_threads_;
};

BENCHMARK(BM_MultiThreaded)
  ->ThreadRunner([](int num_threads) {
    return std::make_unique<OpenMPThreadRunner>(num_threads);
  })
  ->Threads(1)->Threads(2)->Threads(4);

The above example creates a parallel OpenMP region before it enters BM_MultiThreaded.
The actual benchmark code can remain the same and is therefore not tied to a specific
thread runner. The measurement does not include the time for creating and joining the
threads.

CPU Timers

By default, the CPU timer only measures the time spent by the main thread.
If the benchmark itself uses threads internally, this measurement may not
be what you are looking for. Instead, there is a way to measure the total
CPU usage of the process, by all the threads.

c++
void callee(int i);

static void MyMain(int size) {
#pragma omp parallel for
  for(int i = 0; i < size; i++)
    callee(i);
}

static void BM_OpenMP(benchmark::State& state) {
  for (auto _ : state)
    MyMain(state.range(0));
}

// Measure the time spent by the main thread, use it to decide for how long to
// run the benchmark loop. Depending on the internal implementation detail may
// measure to anywhere from near-zero (the overhead spent before/after work
// handoff to worker thread[s]) to the whole single-thread time.
BENCHMARK(BM_OpenMP)->Range(8, 8<<10);

// Measure the user-visible time, the wall clock (literally, the time that
// has passed on the clock on the wall), use it to decide for how long to
// run the benchmark loop. This will always be meaningful, and will match the
// time spent by the main thread in single-threaded case, in general decreasing
// with the number of internal threads doing the work.
BENCHMARK(BM_OpenMP)->Range(8, 8<<10)->UseRealTime();

// Measure the total CPU consumption, use it to decide for how long to
// run the benchmark loop. This will always measure to no less than the
// time spent by the main thread in single-threaded case.
BENCHMARK(BM_OpenMP)->Range(8, 8<<10)->MeasureProcessCPUTime();

// A mixture of the last two. Measure the total CPU consumption, but use the
// wall clock to decide for how long to run the benchmark loop.
BENCHMARK(BM_OpenMP)->Range(8, 8<<10)->MeasureProcessCPUTime()->UseRealTime();

Controlling Timers

Normally, the entire duration of the work loop (for (auto _ : state) {})
is measured. But sometimes, it is necessary to do some work inside of
that loop, every iteration, but without counting that time to the benchmark time.
That is possible, although it is not recommended, since it has high overhead.

c++
static void BM_SetInsert_With_Timer_Control(benchmark::State& state) {
  std::set<int> data;
  for (auto _ : state) {
    state.PauseTiming(); // Stop timers. They will not count until they are resumed.
    data = ConstructRandomSet(state.range(0)); // Do something that should not be measured
    state.ResumeTiming(); // And resume timers. They are now counting again.
    // The rest will be measured.
    for (int j = 0; j < state.range(1); ++j)
      data.insert(RandomNumber());
  }
}
BENCHMARK(BM_SetInsert_With_Timer_Control)->Ranges({{1<<10, 8<<10}, {128, 512}});

Manual Timing

For benchmarking something for which neither CPU time nor real-time are
correct or accurate enough, completely manual timing is supported using
the UseManualTime function.

When UseManualTime is used, the benchmarked code must call
SetIterationTime once per iteration of the benchmark loop to
report the manually measured time.

An example use case for this is benchmarking GPU execution (e.g. OpenCL
or CUDA kernels, OpenGL or Vulkan or Direct3D draw calls), which cannot
be accurately measured using CPU time or real-time. Instead, they can be
measured accurately using a dedicated API, and these measurement results
can be reported back with SetIterationTime.

c++
static void BM_ManualTiming(benchmark::State& state) {
  int microseconds = state.range(0);
  std::chrono::duration<double, std::micro> sleep_duration {
    static_cast<double>(microseconds)
  };

  for (auto _ : state) {
    auto start = std::chrono::high_resolution_clock::now();
    // Simulate some useful workload with a sleep
    std::this_thread::sleep_for(sleep_duration);
    auto end = std::chrono::high_resolution_clock::now();

    auto elapsed_seconds =
      std::chrono::duration_cast<std::chrono::duration<double>>(
        end - start);

    state.SetIterationTime(elapsed_seconds.count());
  }
}
BENCHMARK(BM_ManualTiming)->Range(1, 1<<17)->UseManualTime();

Setting the Time Unit

If a benchmark runs a few milliseconds it may be hard to visually compare the
measured times, since the output data is given in nanoseconds per default. In
order to manually set the time unit, you can specify it manually:

c++
BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);

Additionally the default time unit can be set globally with the
--benchmark_time_unit={ns|us|ms|s} command line argument. The argument only
affects benchmarks where the time unit is not set explicitly.

Preventing Optimization

To prevent a value or expression from being optimized away by the compiler
the benchmark::DoNotOptimize(...) and benchmark::ClobberMemory()
functions can be used.

c++
static void BM_test(benchmark::State& state) {
  for (auto _ : state) {
      int x = 0;
      for (int i=0; i < 64; ++i) {
        benchmark::DoNotOptimize(x += i);
      }
  }
}

DoNotOptimize(<expr>) forces the result of <expr> to be stored in either
memory or a register. For GNU based compilers it acts as read/write barrier
for global memory. More specifically it forces the compiler to flush pending
writes to memory and reload any other values as necessary.

Note that DoNotOptimize(<expr>) does not prevent optimizations on <expr>
in any way. <expr> may even be removed entirely when the result is already
known. For example:

c++
// Example 1: `<expr>` is removed entirely.
  int foo(int x) { return x + 42; }
  while (...) DoNotOptimize(foo(0)); // Optimized to DoNotOptimize(42);

  // Example 2: Result of '<expr>' is only reused.
  int bar(int) __attribute__((const));
  while (...) DoNotOptimize(bar(0)); // Optimized to:
  // int __result__ = bar(0);
  // while (...) DoNotOptimize(__result__);

The second tool for preventing optimizations is ClobberMemory(). In essence
ClobberMemory() forces the compiler to perform all pending writes to global
memory. Memory managed by block scope objects must be "escaped" using
DoNotOptimize(...) before it can be clobbered. In the below example
ClobberMemory() prevents the call to v.push_back(42) from being optimized
away.

c++
static void BM_vector_push_back(benchmark::State& state) {
  for (auto _ : state) {
    std::vector<int> v;
    v.reserve(1);
    auto data = v.data();           // Allow v.data() to be clobbered. Pass as non-const
    benchmark::DoNotOptimize(data); // lvalue to avoid undesired compiler optimizations
    v.push_back(42);
    benchmark::ClobberMemory(); // Force 42 to be written to memory.
  }
}

Note that ClobberMemory() is only available for GNU or MSVC based compilers.

Statistics: Reporting the Mean, Median and Standard Deviation / Coefficient of variation of Repeated Benchmarks

By default each benchmark is run once and that single result is reported.
However benchmarks are often noisy and a single result may not be representative
of the overall behavior. For this reason it's possible to repeatedly rerun the
benchmark.

The number of runs of each benchmark is specified globally by the
--benchmark_repetitions flag or on a per benchmark basis by calling
Repetitions on the registered benchmark object. When a benchmark is run more
than once the mean, median, standard deviation and coefficient of variation
of the runs will be reported.

Additionally the --benchmark_report_aggregates_only={true|false},
--benchmark_display_aggregates_only={true|false} flags or
ReportAggregatesOnly(bool), DisplayAggregatesOnly(bool) functions can be
used to change how repeated tests are reported. By default the result of each
repeated run is reported. When report aggregates only option is true,
only the aggregates (i.e. mean, median, standard deviation and coefficient
of variation, maybe complexity measurements if they were requested) of the runs
is reported, to both the reporters - standard output (console), and the file.
However when only the display aggregates only option is true,
only the aggregates are displayed in the standard output, while the file
output still contains everything.
Calling ReportAggregatesOnly(bool) / DisplayAggregatesOnly(bool) on a
registered benchmark object overrides the value of the appropriate flag for that
benchmark.

Custom Statistics

While having these aggregates is nice, this may not be enough for everyone.
For example you may want to know what the largest observation is, e.g. because
you have some real-time constraints. This is easy. The following code will
specify a custom statistic to be calculated, defined by a lambda function.

c++
void BM_spin_empty(benchmark::State& state) {
  for (auto _ : state) {
    for (int x = 0; x < state.range(0); ++x) {
      benchmark::DoNotOptimize(x);
    }
  }
}

BENCHMARK(BM_spin_empty)
  ->Repetitions(3) // or add option --benchmark_repetitions=3
  ->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
    return *(std::max_element(std::begin(v), std::end(v)));
  })
  ->Arg(512);

While usually the statistics produce values in time units,
you can also produce percentages:

c++
void BM_spin_empty(benchmark::State& state) {
  for (auto _ : state) {
    for (int x = 0; x < state.range(0); ++x) {
      benchmark::DoNotOptimize(x);
    }
  }
}

BENCHMARK(BM_spin_empty)
  ->Repetitions(3) // or add option --benchmark_repetitions=3
  ->ComputeStatistics("ratio", [](const std::vector<double>& v) -> double {
    return v.front() / v.back();
  }, benchmark::StatisticUnit::kPercentage)
  ->Arg(512);

Memory Usage

It's often useful to also track memory usage for benchmarks, alongside CPU
performance. For this reason, benchmark offers the RegisterMemoryManager
method that allows a custom MemoryManager to be injected.

If set, the MemoryManager::Start and MemoryManager::Stop methods will be
called at the start and end of benchmark runs to allow user code to fill out
a report on the number of allocations, bytes used, etc.

This data will then be reported alongside other performance data, currently
only when using JSON output.

Profiling

It's often useful to also profile benchmarks in particular ways, in addition to
CPU performance. For this reason, benchmark offers the RegisterProfilerManager
method that allows a custom ProfilerManager to be injected.

If set, the ProfilerManager::AfterSetupStart and
ProfilerManager::BeforeTeardownStop methods will be called at the start and
end of a separate benchmark run to allow user code to collect and report
user-provided profile metrics.

Output collected from this profiling run must be reported separately.

Using RegisterBenchmark(name, fn, args...)

The RegisterBenchmark(name, func, args...) function provides an alternative
way to create and register benchmarks.
RegisterBenchmark(name, func, args...) creates, registers, and returns a
pointer to a new benchmark with the specified name that invokes
func(st, args...) where st is a benchmark::State object.

Unlike the BENCHMARK registration macros, which can only be used at the global
scope, the RegisterBenchmark can be called anywhere. This allows for
benchmark tests to be registered programmatically.

Additionally RegisterBenchmark allows any callable object to be registered
as a benchmark. Including capturing lambdas and function objects.

For Example:

c++
auto BM_test = [](benchmark::State& st, auto Inputs) { /* ... */ };

int main(int argc, char** argv) {
  for (auto& test_input : { /* ... */ })
      benchmark::RegisterBenchmark(test_input.name(), BM_test, test_input);
  benchmark::Initialize(&argc, argv);
  benchmark::RunSpecifiedBenchmarks();
  benchmark::Shutdown();
}

Exiting with an Error

When errors caused by external influences, such as file I/O and network
communication, occur within a benchmark the
State::SkipWithError(const std::string& msg) function can be used to skip that run
of benchmark and report the error. Note that only future iterations of the
KeepRunning() are skipped. For the ranged-for version of the benchmark loop
Users must explicitly exit the loop, otherwise all iterations will be performed.
Users may explicitly return to exit the benchmark immediately.

The SkipWithError(...) function may be used at any point within the benchmark,
including before and after the benchmark loop. Moreover, if SkipWithError(...)
has been used, it is not required to reach the benchmark loop and one may return
from the benchmark function early.

For example:

c++
static void BM_test(benchmark::State& state) {
  auto resource = GetResource();
  if (!resource.good()) {
    state.SkipWithError("Resource is not good!");
    // KeepRunning() loop will not be entered.
  }
  while (state.KeepRunning()) {
    auto data = resource.read_data();
    if (!resource.good()) {
      state.SkipWithError("Failed to read data!");
      break; // Needed to skip the rest of the iteration.
    }
    do_stuff(data);
  }
}

static void BM_test_ranged_fo(benchmark::State & state) {
  auto resource = GetResource();
  if (!resource.good()) {
    state.SkipWithError("Resource is not good!");
    return; // Early return is allowed when SkipWithError() has been used.
  }
  for (auto _ : state) {
    auto data = resource.read_data();
    if (!resource.good()) {
      state.SkipWithError("Failed to read data!");
      break; // REQUIRED to prevent all further iterations.
    }
    do_stuff(data);
  }
}

A Faster KeepRunning Loop

A ranged-based for loop should be used in preference to
the KeepRunning loop for running the benchmarks. For example:

c++
static void BM_Fast(benchmark::State &state) {
  for (auto _ : state) {
    FastOperation();
  }
}
BENCHMARK(BM_Fast);

The reason the ranged-for loop is faster than using KeepRunning, is
because KeepRunning requires a memory load and store of the iteration count
ever iteration, whereas the ranged-for variant is able to keep the iteration count
in a register.

For example, an empty inner loop of using the ranged-based for method looks like:

asm
# Loop Init
  mov rbx, qword ptr [r14 + 104]
  call benchmark::State::StartKeepRunning()
  test rbx, rbx
  je .LoopEnd
.LoopHeader: # =>This Inner Loop Header: Depth=1
  add rbx, -1
  jne .LoopHeader
.LoopEnd:

Compared to an empty KeepRunning loop, which looks like:

asm
.LoopHeader: # in Loop: Header=BB0_3 Depth=1
  cmp byte ptr [rbx], 1
  jne .LoopInit
.LoopBody: # =>This Inner Loop Header: Depth=1
  mov rax, qword ptr [rbx + 8]
  lea rcx, [rax + 1]
  mov qword ptr [rbx + 8], rcx
  cmp rax, qword ptr [rbx + 104]
  jb .LoopHeader
  jmp .LoopEnd
.LoopInit:
  mov rdi, rbx
  call benchmark::State::StartKeepRunning()
  jmp .LoopBody
.LoopEnd:

Unless C++03 compatibility is required, the ranged-for variant of writing
the benchmark loop should be preferred.

Disabling CPU Frequency Scaling

If you see this error:

text
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may
be noisy and will incur extra overhead.

you might want to disable the CPU frequency scaling while running the
benchmark, as well as consider other ways to stabilize the performance of
your system while benchmarking.

See Reducing Variance for more information.


Thirdparty Sentry Native External Breakpad Docs Client Design (`thirdparty/sentry-native/external/breakpad/docs/client_design.md`)

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 will aid in understanding the
handler.

Overview

Breakpad provides client libraries for each of its target platforms. Currently,
these exist for Windows on x86 and Mac OS X on both x86 and PowerPC. A Linux
implementation has been written and is currently under review.

Because the mechanisms for catching exceptions and the methods for obtaining the
information that a dump contains vary between operating systems, each target
operating system requires a completely different handler implementation. Where
multiple CPUs are supported for a single operating system, the handler
implementation will likely also require separate code for each processor type to
extract CPU-specific information. One of the goals of the Breakpad handler is to
provide a prepackaged cross-platform system that masks many of these
system-level differences and quirks from the application developer. Although the
underlying implementations differ, the handler library for each system follows
the same set of principles and exposes a similar interface.

Code that wishes to take advantage of Breakpad should be linked against the
handler library, and should, at an appropriate time, install a Breakpad handler.
For applications, it is generally desirable to install the handler as early in
the start-up process as possible. Developers of library code using Breakpad to
monitor itself may wish to install a Breakpad handler when the library is
loaded, or may only want to install a handler when calls are made in to the
library.

The handler can be triggered to generate a dump either by catching an exception
or at the request of the application itself. The latter case may be useful in
debugging assertions or other conditions where developers want to know how a
program got in to a specific non-crash state. After generating a dump, the
handler calls a user-specified callback function. The callback function may
collect additional data about the program’s state, quit the program, launch a
crash reporter application, or perform other tasks. Allowing for this
functionality to be dictated by a callback function preserves flexibility.

The sender library is also has a separate implementation for each supported
platform, because of the varying interfaces for accessing network resources on
different operating systems. The sender transmits a dump along with other
application-defined information to a crash report server via HTTP. Because dumps
may contain sensitive data, the sender allows for the use of HTTPS.

The canonical example of the entire client system would be for a monitored
application to link against the handler library, install a Breakpad handler from
its main function, and provide a callback to launch a small crash reporter
program. The crash reporter program would be linked against the sender library,
and would send the crash dump when launched. A separate process is recommended
for this function because of the unreliability inherent in doing any significant
amount of work from a crashed process.

Detailed Design

Exception Handler Installation

The mechanisms for installing an exception handler vary between operating
systems. On Windows, it’s a relatively simple matter of making one call to
register a top-level exception
filter

callback function. On most Unix-like systems such as Linux, processes are
informed of exceptions by the delivery of a signal, so an exception handler
takes the form of a signal handler. The native mechanism to catch exceptions on
Mac OS X requires a large amount of code to set up a Mach port, identify it as
the exception port, and assign a thread to listen for an exception on that port.
Just as the preparation of exception handlers differ, the manner in which they
are called differs as well. On Windows and most Unix-like systems, the handler
is called on the thread that caused the exception. On Mac OS X, the thread
listening to the exception port is notified that an exception has occurred. The
different implementations of the Breakpad handler libraries perform these tasks
in the appropriate ways on each platform, while exposing a similar interface on
each.

A Breakpad handler is embodied in an ExceptionHandler object. Because it’s a
C++ object, ExceptionHandlers may be created as local variables, allowing them
to be installed and removed as functions are called and return. This provides
one possible way for a developer to monitor only a portion of an application for
crashes.

Exception Basics

Once an application encounters an exception, it is in an indeterminate and
possibly hazardous state. Consequently, any code that runs after an exception
occurs must take extreme care to avoid performing operations that might fail,
hang, or cause additional exceptions. This task is not at all straightforward,
and the Breakpad handler library seeks to do it properly, accounting for all of
the minute details while allowing other application developers, even those with
little systems programming experience, to reap the benefits. All of the Breakpad
handler code that executes after an exception occurs has been written according
to the following guidelines for safety at exception time:

  • Use of the application heap is forbidden. The heap may be corrupt or
    otherwise unusable, and allocators may not function.
  • Resource allocation must be severely limited. The handler may create a new
    file to contain the dump, and it may attempt to launch a process to continue
    handling the crash.
  • Execution on the thread that caused the exception is significantly limited.
    The only code permitted to execute on this thread is the code necessary to
    transition handling to a dedicated preallocated handler thread, and the code
    to return from the exception handler.
  • Handlers shouldn’t handle crashes by attempting to walk stacks themselves,
    as stacks may be in inconsistent states. Dump generation should be performed
    by interfacing with the operating system’s memory manager and code module
    manager.
  • Library code, including runtime library code, must be avoided unless it
    provably meets the above guidelines. For example, this means that the STL
    string class may not be used, because it performs operations that attempt to
    allocate and use heap memory. It also means that many C runtime functions
    must be avoided, particularly on Windows, because of heap operations that
    they may perform.

A dedicated handler thread is used to preserve the state of the exception thread
when an exception occurs: during dump generation, it is difficult if not
impossible for a thread to accurately capture its own state. Performing all
exception-handling functions on a separate thread is also critical when handling
stack-limit-exceeded exceptions. It would be hazardous to run out of stack space
while attempting to handle an exception. Because of the rule against allocating
resources at exception time, the Breakpad handler library creates its handler
thread when it installs its exception handler. On Mac OS X, this handler thread
is created during the normal setup of the exception handler, and the handler
thread will be signaled directly in the event of an exception. On Windows and
Linux, the handler thread is signaled by a small amount of code that executes on
the exception thread. Because the code that executes on the exception thread in
this case is small and safe, this does not pose a problem. Even when an
exception is caused by exceeding stack size limits, this code is sufficiently
compact to execute entirely within the stack’s guard page without causing an
exception.

The handler thread may also be triggered directly by a user call, even when no
exception occurs, to allow dumps to be generated at any point deemed
interesting.

Filter Callback

When the handler thread begins handling an exception, it calls an optional
user-defined filter callback function, which is responsible for judging whether
Breakpad’s handler should continue handling the exception or not. This mechanism
is provided for the benefit of library or plug-in code, whose developers may not
be interested in reports of crashes that occur outside of their modules but
within processes hosting their code. If the filter callback indicates that it is
not interested in the exception, the Breakpad handler arranges for it to be
delivered to any previously-installed handler.

Dump Generation

Assuming that the filter callback approves (or does not exist), the handler
writes a dump in a directory specified by the application developer when the
handler was installed, using a previously generated unique identifier to avoid
name collisions. The mechanics of dump generation also vary between platforms,
but in general, the process involves enumerating each thread of execution, and
capturing its state, including processor context and the active portion of its
stack area. The dump also includes a list of the code modules loaded in to the
application, and an indicator of which thread generated the exception or
requested the dump. In order to avoid allocating memory during this process, the
dump is written in place on disk.

Post-Dump Behavior

Upon completion of writing the dump, a second callback function is called. This
callback may be used to launch a separate crash reporting program or to collect
additional data from the application. The callback may also be used to influence
whether Breakpad will treat the exception as handled or unhandled. Even after a
dump is successfully generated, Breakpad can be made to behave as though it
didn’t actually handle an exception. This function may be useful for developers
who want to test their applications with Breakpad enabled but still retain the
ability to use traditional debugging techniques. It also allows a
Breakpad-enabled application to coexist with a platform’s native crash reporting
system, such as Mac OS X’ CrashReporter
and Windows Error Reporting.

Typically, when Breakpad handles an exception fully and no debuggers are
involved, the crashed process will terminate.

Authors of both callback functions that execute within a Breakpad handler are
cautioned that their code will be run at exception time, and that as a result,
they should observe the same programming practices that the Breakpad handler
itself adheres to. Notably, if a callback is to be used to collect additional
data from an application, it should take care to read only β€œsafe” data. This
might involve accessing only static memory locations that are updated
periodically during the course of normal program execution.

Sender Library

The Breakpad sender library provides a single function to send a crash report to
a crash server. It accepts a crash server’s URL, a map of key-value parameters
that will accompany the dump, and the path to a dump file itself. Each of the
key-value parameters and the dump file are sent as distinct parts of a multipart
HTTP POST request to the specified URL using the platform’s native HTTP
facilities. On Linux, libcurl is used for this function,
as it is the closest thing to a standard HTTP library available on that
platform.

Future Plans

Although we’ve had great success with in-process dump generation by following
our guidelines for safe code at exception time, we are exploring options for
allowing dumps to be generated in a separate process, to further enhance the
handler library’s robustness.

On Windows, we intend to offer tools to make it easier for Breakpad’s settings
to be managed by the native group policy management system.

We also plan to offer tools that many developers would find desirable in the
context of handling crashes, such as a mechanism to determine at launch if the
program last terminated in a crash, and a way to calculate β€œcrashiness” in terms
of crashes over time or the number of application launches between crashes.

We are also investigating methods to capture crashes that occur early in an
application’s launch sequence, including crashes that occur before a program’s
main function begins executing.


Thirdparty Sentry Native External Breakpad Docs Contributing To Breakpad (`thirdparty/sentry-native/external/breakpad/docs/contributing_to_breakpad.md`)

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.

If you're doing work individually, not as part of any employment, you'll need to
sign the Individual
Contributor License Agreement
. This agreement can be completed
electronically.

If you're contributing to Breakpad as part of your employment with another
organization, you'll need to sign a Corporate
Contributor License Agreement
. Once completed this document will need to be
faxed.

IMPORTANT: The authors(you!) of the contributions will maintain all
copyrights; the agreements you sign will grant rights to Google to use your
work.

Thanks, and if you have any questions let me know and I'll loop in the legal guy
here to get you an answer.

Technical

Once you have signed the agreement you can be added to our contributors list and
have write access to code. For full details on getting started see our trunk
README.

List of people who have signed contributor agreements

None so far.


Thirdparty Sentry Native External Breakpad Docs Exception Handling (`thirdparty/sentry-native/external/breakpad/docs/exception_handling.md`)

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.


Thirdparty Sentry Native External Breakpad Docs Getting Started With Breakpad (`thirdparty/sentry-native/external/breakpad/docs/getting_started_with_breakpad.md`)

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.

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
    .

  • 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
https://chromium.googlesource.com/breakpad/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)

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.


Thirdparty Sentry Native External Breakpad Docs Linux Core Handler (`thirdparty/sentry-native/external/breakpad/docs/linux_core_handler.md`)

How To Use Breakpad As a Coredump Handler on Linux

This document presents a way to use Breakpad in order to generate
minidumps system wide on Linux.

Please refer to Linux starter guide if
instead you want to integrate breakpad into your application.

Motivation

When working on an embedded system, disk and memory space is often
limited and when a process crashes it must be restarted as soon as
possible. Sometime saving a full coredump takes to much time or
consumes too much space.

Breakpad Core Handler

In such case the program core_handler can be use to generate
minidumps instead of coredumps. core_handler reads the firsts
sections of the coredump (where the various threads are described)
generated by Linux from the standard input and then directly reads
/proc//mem to reconstruct the stacktraces.

One can test it with:

text
# echo "|/usr/libexec/core_handler %P /var/lib/minidump/%e-%i.md" >
                /proc/sys/kernel/core_pattern
# echo 1 > /proc/sys/kernel/core_pipe_limit

Be aware that a real world integration would likely require further
customization and so core_handler can be wrapped into a script (for
example to change the permission of the minidump file or to signal the
presence of the minidump to another service).

Please refer to
core(5) for more
details.


Thirdparty Sentry Native External Breakpad Docs Linux Starter Guide (`thirdparty/sentry-native/external/breakpad/docs/linux_starter_guide.md`)

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:

cpp
#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:

cpp
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context, bool succeeded) {
  printf("Dump path: %s\n", descriptor.path());
  return succeeded;
}

void crash() { volatile int* a = (int*)(NULL); *a = 1; }

int main(int argc, char* argv[]) {
  google_breakpad::MinidumpDescriptor descriptor("/tmp");
  google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
  crash();
  return 0;
}

Compiling and running this example should produce a minidump file in /tmp, and
it should print the minidump filename before exiting. You can read more about
the other parameters to the ExceptionHandler constructor in the exception_handler.h source file.

Note: You should do as little work as possible in the callback function.
Your application is in an unsafe state. It may not be safe to allocate memory or
call functions from other shared libraries. The safest thing to do is fork and
exec a new process to do any work you need to do. If you must do some work in
the callback, the Breakpad source contains some simple reimplementations of libc functions, to avoid calling directly into
libc, as well as a header file for making Linux system calls (in src/third_party/lss) to avoid calling into other shared libraries.

Sending the minidump file

In a real application, you would want to handle the minidump in some way, likely
by sending it to a server for analysis. The Breakpad source tree contains some
HTTP upload source
that you might find useful, as well as a minidump upload tool.

Producing symbols for your application

To produce useful stack traces, Breakpad requires you to convert the debugging
symbols in your binaries to text-format symbol files. First, ensure that you've compiled your binaries with -g to
include debugging symbols. Next, compile the dump_syms tool by running
configure && make in the Breakpad source directory. Next, run dump_syms on
your binaries to produce the text-format symbols. For example, if your main
binary was named test:

text
$ 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):

text
$ head -n1 test.sym MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test
$ mkdir -p ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
$ mv test.sym ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830

You may also find the symbolstore.py script in the Mozilla repository useful, as it encapsulates these steps.

Processing the minidump to produce a stack trace

Breakpad includes a tool called minidump_stackwalk which can take a minidump
plus its corresponding text-format symbols and produce a symbolized stacktrace.
It should be in the google-breakpad/src/processor directory if you compiled
the Breakpad source using the directions above. Simply pass it the minidump and
the symbol path as commandline parameters:

text
$ 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.


Thirdparty Sentry Native External Breakpad Docs Linux System Calls (`thirdparty/sentry-native/external/breakpad/docs/linux_system_calls.md`)

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.


Thirdparty Sentry Native External Breakpad Docs Mac Breakpad Starter Guide (`thirdparty/sentry-native/external/breakpad/docs/mac_breakpad_starter_guide.md`)

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:

text
#!/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.
text
#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
text
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:

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


Thirdparty Sentry Native External Breakpad Docs Mozilla Brown Bag Talk (`thirdparty/sentry-native/external/breakpad/docs/mozilla_brown_bag_talk.md`)

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
    • HTTPS 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

More Information


Thirdparty Sentry Native External Breakpad Docs Processor Design (`thirdparty/sentry-native/external/breakpad/docs/processor_design.md`)

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’s Bug-Buddy and
Apple’s
CrashReporter,
require symbolic
information to be present on the end user’s computer; in the case of
CrashReporter, the reports are transmitted only to Apple, not to third-party
developers. Other systems, such as Microsoft’s
Windows Error Reporting and
SupportSoft’s Talkback, transmit only a snapshot of a crashed process’ state,
which can later be combined with symbolic debugging information without the need
for it to be present on end users’ computers. Because symbolic debugging
information consumes a large amount of space and is otherwise not needed during
the normal operation of software, and because some developers are reluctant to
release debugging symbols to their customers, Breakpad follows the latter
approach.

We know of no currently-maintained crash-reporting systems that meet our
requirements, which are to: * allow for symbols to be separate from the
application, * handle crash reports from multiple platforms, * allow developers
to operate their own crash-reporting platform, and to * be open-source. Windows
Error Reporting only functions for Microsoft products, and requires the
involvement of Microsoft’s servers. Talkback, while cross-platform, has not been
maintained and at this point does not support Mac OS X on x86, which we consider
to be a significant platform. Talkback is also closed-source commercial
software, and has very specific requirements for its server platform.

We are aware of Windows-only crash-reporting systems that leverage Microsoft’s
debugging interfaces. Such systems, even if extended to support dumps from other
platforms, are tied to using Windows for at least a portion of the processor
platform.

Overview

The Breakpad processor itself is written in standard C++ and will work on a
variety of platforms. The dumps it accepts may also have been created on a
variety of systems. The library is able to combine dumps with symbolic debugging
information to create stack traces that include function signatures. The
processor library includes simple command-line tools to examine dumps and
process them, producing stack traces. It also exposes several layers of APIs
enabling crash-reporting systems to be built around the Breakpad processor.

Detailed Design

Dump Files

In the processor, the dump data is of primary significance. Dumps typically
contain:

  • CPU context (register data) as it was at the time the crash occurred, and an
    indication of which thread caused the crash. General-purpose registers are
    included, as are special-purpose registers such as the instruction pointer
    (program counter).
  • Information about each thread of execution within a crashed process,
    including:
    • The memory region used for each thread’s stack.
    • CPU context for each thread, which for various reasons is not the same
      as the crash context in the case of the crashed thread.
  • A list of loaded code segments (or modules), including:
    • The name of the file (.so, .exe, .dll, etc.) which provides the
      code.
    • The boundaries of the memory region in which the code segment is visible
      to the process.
    • A reference to the debugging information for the code module, when such
      information is available.

Ordinarily, dumps are produced as a result of a crash, but other triggers may be
set to produce dumps at any time a developer deems appropriate. The Breakpad
processor can handle dumps in the minidump format, either generated by an
Breakpad client β€œhandler” implementation, or by another
implementation that produces dumps in this format. The
DbgHelp.dll!MiniDumpWriteDump
function on Windows
produces dumps in this format, and is the basis for the Breakpad handler
implementation on that platform.

The minidump format is
essentially a simple container format, organized as a series of streams. Each
stream contains some type of data relevant to the crash. A typical β€œnormal”
minidump contains streams for the thread list, the module list, the CPU context
at the time of the crash, and various bits of additional system information.
Other types of minidump can be generated, such as a full-memory minidump, which
in addition to stack memory contains snapshots of all of a process’ mapped
memory regions.

The minidump format was chosen as Breakpad’s dump format because it has an
established track record on Windows, and it can be adapted to meet the needs of
the other platforms that Breakpad supports. Most other operating systems use
β€œcore” files as their native dump formats, but the capabilities of core files
vary across platforms, and because core files are usually presented in a
platform’s native executable format, there are complications involved in
accessing the data contained therein without the benefit of the header files
that define an executable format’s entire structure. Because minidumps are
leaner than a typical executable format, a redefinition of the format in a
cross-platform header file, minidump_format.h, was a straightforward task.
Similarly, the capabilities of the minidump format are understood, and because
it provides an extensible container, any of Breakpad’s needs that could not be
met directly by the standard minidump format could likely be met by extending it
as needed. Finally, using this format means that the dump file is compatible
with native debugging tools at least on Windows. A possible future avenue for
exploration is the conversion of minidumps to core files, to enable this same
benefit on other platforms.

We have already provided an extension to the minidump format that allows it to
carry dumps generated on systems with PowerPC processors. The format already
allows for variable CPUs, so our work in this area was limited to defining a
context structure sufficient to represent the execution state of a PowerPC. We
have also defined an extension that allows minidumps to indicate which thread of
execution requested a dump be produced for non-crash dumps.

Often, the information contained within a dump alone is sufficient to produce a
full stack backtrace for each thread. Certain optimizations that compilers
employ in producing code frustrate this process. Specifically, the β€œframe
pointer omission” optimization of x86 compilers can make it impossible to
produce useful stack traces given only a stack snapshot and CPU context. In
these cases, however, compiler-emitted debugging information can aid in
producing useful stack traces. The Breakpad processor is able to take advantage
of this debugging information as supplied by Microsoft’s C/C++ compiler, the
only compiler to apply such optimizations by default. As a result, the Breakpad
processor can produce useful stack traces even from code with frame pointer
omission optimizations as produced by this compiler.

Symbol Files

The symbol files that the Breakpad processor accepts allow
for frame pointer omission data, but this is only one of their capabilities.
Each symbol file also includes information about the functions, source files,
and source code line numbers for a single module of code. A module is an
individually-loadble chunk of code: these can be executables containing a main
program (exe files on Windows) or shared libraries (.so files on Linux,
.dylib files, frameworks, and bundles on Mac OS X, and .dll files on
Windows). Dumps contain information about which of these modules were loaded at
the time the dump was produced, and given this information, the Breakpad
processor attempts to locate debugging symbols for the module through a
user-supplied function embodied in a β€œsymbol supplier.” Breakpad includes a
sample symbol supplier, called SimpleSymbolSupplier, that is used by its
command-line tools; this supplier locates symbol files by pathname.
SimpleSymbolSupplier is also available to other users of the Breakpad
processor library. This allows for the use of a simple reference implementation,
but preserves flexibility for users who may have more demanding symbol file
storage needs.

Breakpad’s symbol file format is text-based, and was defined to be fairly
human-readable and to encompass the needs of multiple platforms. The Breakpad
processor itself does not operate directly with native symbol formats
(DWARF and
STABS
on most Unix-like systems,
.pdb files
on Windows),
because of the complications in accessing potentially complex symbol formats
with slight variations between platforms, stored within different types of
binary formats. In the case of .pdb files, the debugging format is not even
documented. Instead, Breakpad’s symbol files are produced on each platform,
using specific debugging APIs where available, to convert native symbols to
Breakpad’s cross-platform format.

Processing

Most commonly, a developer will enable an application to use Breakpad by
building it with a platform-specific client β€œhandler”
library. After building the application, the developer will create symbol files
for Breakpad’s use using the included dump_syms or symupload tools, or
another suitable tool, and place the symbol files where the processor’s symbol
supplier will be able to locate them.

When a dump file is given to the processor’s MinidumpProcessor class, it will
read it using its included minidump reader, contained in the Minidump family
of classes. It will collect information about the operating system and CPU that
produced the dump, and determine whether the dump was produced as a result of a
crash or at the direct request of the application itself. It then loops over all
of the threads in a process, attempting to walk the stack associated with each
thread. This process is achieved by the processor’s Stackwalker components, of
which there are a slightly different implementations for each CPU type that the
processor is able to handle dumps from. Beginning with a thread’s context, and
possibly using debugging data, the stackwalker produces a list of stack frames,
containing each instruction executed in the chain. These instructions are
matched up with the modules that contributed them to a process, and the
SymbolSupplier is invoked to locate a symbol file. The symbol file is given to
a SourceLineResolver, which matches the instruction up with a specific
function name, source file, and line number, resulting in a representation of a
stack frame that can easily be used to identify which code was executing.

The results of processing are made available in a ProcessState object, which
contains a vector of threads, each containing a vector of stack frames.

For small-scale use of the Breakpad processor, and for testing and debugging,
the minidump_stackwalk tool is provided. It invokes the processor and displays
the full results of processing, optionally allowing symbols to be provided to
the processor by a pathname-based symbol supplier, SimpleSymbolSupplier.

For lower-level testing and debugging, the processor library also includes a
minidump_dump tool, which walks through an entire minidump file and displays
its contents in somewhat readable form.

Platform Support

The Breakpad processor library is able to process dumps produced on Mac OS X
systems running on x86, x86-64, and PowerPC processors, on Windows and Linux
systems running on x86 or x86-64 processors, and on Android systems running ARM
or x86 processors. The processor library itself is written in standard C++, and
should function properly in most Unix-like environments. It has been tested on
Linux and Mac OS X.

Future Plans

There are currently no firm plans or timetables to implement any of these
features, although they are possible avenues for future exploration.

The symbol file format can be extended to carry information about the locations
of parameters and local variables as stored in stack frames and registers, and
the processor can use this information to provide enhanced stack traces showing
function arguments and variable values.

On Mac OS X and Linux, we can provide tools to convert files from the minidump
format into the native core format. This will enable developers to open dump
files in a native debugger, just as they are presently able to do with minidumps
on Windows.


Thirdparty Sentry Native External Breakpad Docs Stack Walking (`thirdparty/sentry-native/external/breakpad/docs/stack_walking.md`)

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
class and calling the MinidumpProcessor::Process
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
and a SourceLineResolverInterface.
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),
the list of modules loaded in the process
(MinidumpModuleList),
and information about the exception that caused the process to crash
(MinidumpException).

Enumerating Threads

For each thread in the thread list
(MinidumpThread),
the thread memory containing the stack for the thread
(MinidumpMemoryRegion)
and the CPU context representing the CPU state of the thread at the time the
dump was written (MinidumpContext)
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
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
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 method.

Locating Symbols

If a module is located, the SymbolSupplier is asked to locate symbols
corresponding to the module by calling its
GetCStringSymbolData
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
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
method. The BasicSourceLineResolver
implementation parses the text-format symbol file 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
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 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 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 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
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
and FindCFIFrameInfo
methods. These methods look for Windows unwind info extracted from a PDB file
(STACK WIN lines from the symbol file), or
DWARF CFI extracted from a binary (STACK CFI
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
method) and using a heuristic to attempt to find a reasonable return address
(implemented in the
Stackwalker::InstructionAddressSeemsValid 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.


Thirdparty Sentry Native External Breakpad Docs Sym Upload V2 Protocol (`thirdparty/sentry-native/external/breakpad/docs/sym_upload_v2_protocol.md`)

Introduction

The sym_upload tool is able to operate in sym-upload-v2 protocol mode, in
addition to the legacy protocol (which will be referred to as sym-upload-v1
for the rest of this document). For now sym-upload-v2 is HTTP/REST-based but
it could be extended to operate over gRPC instead, in the future.

Table of Contents

Why

Using sym_upload in sym-upload-v2 protocol mode has the following features
beyond sym-upload-v1:

  • Authentication via key (arbitrary secret).
  • Symbol identifier (product of debug_file and debug_id, as recorded in
    output from dump_syms) can be checked against existing symbol information on
    server. If it's present, then the upload is skipped entirely.

How

Uploading

Uploading with `sym_upload`

Uploading in sym-upload-v2 protocol mode is easy. Invoke sym_upload like

text
$ ./sym_upload -p sym-upload-v2 [-k <API-key>] <symbol-file> <API-URL>

Where symbol-file is a symbol file created by dump_syms, API-URL is the
URL of your sym-upload-v2 API service (see next section for details), and
API-key is a secret known to your uploader and server.

For more options see sym_upload --help.

Uploading with curl

As an example, if:

  • Your API's URL was "https://sym-upload-api".
  • Your service has assigned you key "myfancysecret123".
  • You wanted to upload the symbol file at "path/to/file_name", with
    debug_file being "file_name" and debug_id being
    "123123123123123123123123123". Normally you would read these values from
    "path/to/file_name", which in turn was generated by dump_syms.

Then you might run:

text
$ curl https://sym-upload-api/symbols/file_name/123123123123123123123123123:checkStatus?key=myfancysecret123

And, upon seeing that this debug_file/debug_id combo is missing from symbol
storage then you could run:

text
$ curl --request POST https://sym-upload-api/uploads:create?key=myfancysecret123

Which returns upload_url "https://upload-server/42?creds=shhhhh" and
upload_key "42". Next you upload the file directly like:

text
$ curl -T path/to/file_name "https://upload-server/42?creds=shhhhh"

Once the HTTP PUT is complete, run:

text
$ curl --header "Content-Type: application/json" \
    --request POST \
    --data '{symbol_id:{"debugFile":"file_name",'\
        '"debugId":"123123123123123123123123123"}}' \
    https://sym-upload-api/uploads/42:complete?key=myfancysecret123

Serving the `sym-upload-v2` Protocol

The protocol is currently defined only in HTTP/REST. There are three necessary
REST operations to implement in your service:

  • /symbols/<debug_file>/<debug_id>:checkStatus?key=<key>
  • /uploads:create?key=<key>
  • /uploads/:complete?key=<key>

Authenticate Using `key`

The query string arg key contains some secret that both the uploader and
server understand. It is up to the service implementer to decide on what
constitutes a valid key, how the uploader acquires one, and how to handle
requests made with invalid ones.

Symbol `checkStatus`

text
/symbols/<debug_file>/<debug_id>:checkStatus?key=<key>

This operation expects an empty (or no) JSON payload in the request.

This operation should return the status of the symbol file uniquely identified
by the given debug_file and debug_id. JSON schema:

text
{
    "type": object",
    "properties": {
        "status": {
            "type": "string",
            "enum": ["STATUS_UNSPECIFIED", "MISING", "FOUND"],
            "required": true
        }
    }
}

Where MISSING denotes that the symbol file does not exist on the server and
FOUND denotes that the symbol file exists on the server.

Upload `create`

text
/uploads:create?key=<key>

This operation expects an empty (or no) JSON payload in the request.

This operation should return a URL that uploader can HTTP PUT their symbol file
to, along with an "upload key" that can be used to notify the service once the
file upload is completed. JSON schema:

text
{
    "type": "object",
    "properties": {
        "upload_url": {
            "type: "string",
            "required": true
        },
        "upload_key": {
            "type": "string",
            "required": true
        }
    }
}

Since this REST API operation can be authenticated via the key query string
arg, the service can return a URL that encodes permission delegation to the
upload endpoint resource and thereby constrain the ability to upload to those
with valid keys.

Uploading the Symbol File

Note that the actual symbol upload step is not part of the REST API. The
upload URL obtained in the above operation is meant to be used as the endpoint
for a normal HTTP PUT request for the contents of the symbol file. Once that
HTTP PUT request is completed use the upload complete operation.

Upload `complete`

text
/uploads/<upload_key>:complete?key=<key>

This operation expects a JSON payload in the HTTP request body with the
following schema:

text
{
    "type": "object",
    "properties": {
        "symbol_id": {
            "type": "object",
            "properties": {
                "debug_file": {
                    "type": "string",
                    "required": true
                },
                "debug_id": {
                    "type": "string",
                    "required": true
                }
            }
        }
    }
}

This operation should cause the symbol storage back-end (however implemented)
to consume the symbol file identified by upload_key. It is up to the service
implementation to decide how uploads are assigned upload_keys and how to
retrieve a completed upload by its upload_key. If the symbol file cannot be
found, is malformed, or the operation cannot be completed for any other reason
then an HTTP error will be returned. JSON schema of non-error responses:

text
{
    "type": "object",
    "properties": {
        "result": {
            "type": string,
            "enum": ["RESULT_UNSPECIFIED", "OK", "DUPLICATE_DATA"],
            "required": true
        }
    }
}

Where OK denotes that the symbol storage was updated with the new symbol file
and DUPLICATE_DATA denotes that the symbol file data was identical to data
already in symbol storage and therefore nothing changed.


Thirdparty Sentry Native External Breakpad Docs Symbol Files (`thirdparty/sentry-native/external/breakpad/docs/symbol_files.md`)

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.

  • An INLINE_ORIGIN record holds an inline function name for INLINE records
    to refer to.

  • A FUNC record describes a function present in the source code.

  • An INLINE record describes the inline function's nest level, call site
    line and call site source file to which the given ranges of machine code
    should be attributed.

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

`INLINE_ORIGIN` records

An INLINE_ORIGIN record holds an inline function name for INLINE records to
refer to. It has the form:

INLINE_ORIGIN number name

For example: INLINE_ORIGIN 2 nsQueryInterfaceWithError::operator()(nsID const&, void**) const

An INLINE_ORIGIN record provides the name of an inline function, and assigns
it a number which other records (INLINE records, in particular) can use to
refer to that function name. The number field is a decimal number. The name
field is the name of the inline function; it may contain spaces.

`FUNC` records

A FUNC record describes a source-language function. It has the form:

FUNC [m] address size parameter_size name

For example: FUNC m c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&, void**) const

The m field is optional. If present it indicates that multiple symbols
reference this function's instructions. (In which case, only one symbol name is
mentioned within the breakpad file.) Multiple symbols referencing the same
instructions may occur due to identical code folding by the linker.

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.

`INLINE` records

An INLINE record describes the inline function's nest level, call site line
and call site source file to which the given ranges of machine code should be
attributed. It has the form:

INLINE inline_nest_level call_site_line call_site_file_num
origin_num [address size]+

For example: INLINE 0 10 3 4 d30 2a fa1 b

The inline_nest_level field is a decimal number that means it's inlined at the
function described by a previous INLINE record which has inline_nest_level
one less than its. In the example below, first and third INLINE records have
inline_nest_level 0, which means they are inlined inside the function
described by the FUNC record. The second INLINE record has
inline_nest_level 1 means that it's inlined at the inline function described
by first INLINE record.

text
FUNC ...
INLINE 0 ...
INLINE 1 ...
INLINE 0 ...

The call_site_line and call_site_file_num fields are decimal numbers
indicating where this inline function being called at.

The origin_num field refers to an INLINE_ORIGIN record that has the name
of the inline function.

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. There could be more than one [address size] range
pair, since inline functions could have discontinuous address ranges. The ranges
of an INLINE record are always inside the ranges described by its parent
record (a FUNC record or an INLINE record).

The INLINE record is assumed to belong to the function described by the last
preceding FUNC record. INLINE records may not appear before the first FUNC
record.

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 [m] address parameter_size name

For example: PUBLIC m 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 m field is optional. If present it indicates that multiple symbols
reference this function's instructions. (In which case, only one symbol name is
mentioned within the breakpad file.) Multiple symbols referencing the same
instructions may occur due to identical code folding by the linker.

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
type in Microsoft's
Debug Interface Access (DIA) 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
    header file explain the language in which the program is written. You should
    place the following variables in the dictionary before interpreting the
    program:
    • $ebp and $esp should be the values of the %ebp and %esp
      registers in F.
    • .cbParams, .cbSavedRegs, and .cbLocals, should be the values of
      the STACK WIN record's parameter_size, saved_register_size, and
      local_size fields.
    • .raSearchStart should be set to the address on the stack to begin
      scanning for a return address, if necessary. The Breakpad processor sets
      this to the value of %esp in F, plus the frame_size value mentioned
      above.

If the program stores values for $eip, $esp, $ebp, $ebx, $esi, or
$edi, then those are the values of the given registers in the caller. If the
value of $eip is zero, that indicates that the end of the stack has been
reached.

The Breakpad processor checks that the value yielded by the above for the
calling frame's instruction address refers to known code; if the address seems
to be bogus, then it uses a heuristic search to find F's return address and
stack base.

`STACK CFI` records

STACK CFI ("Call Frame Information") records describe how to walk the stack
when execution is at a given machine instruction. These records take one of two
forms:

STACK CFI INIT address size register1:
expression1 register2: expression2 ...

STACK CFI address register1: expression1
register2: expression2 ...

For example:

text
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
registeri 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:

text
func:                                ; entry point; return address at sp
func+0:      sp -= 16                ; allocate space for stack frame
func+1:      sp[12] = r0             ; save 4-byte r0 at sp+12
             ...                     ; stuff that doesn't affect stack
func+10:     sp -= 4; *sp = x        ; push some 4-byte x on the stack
             ...                     ; stuff that doesn't affect stack
func+20:     r0 = sp[16]             ; restore saved r0
func+21:     sp += 20                ; pop whole stack frame
func+22:     pc = *sp; sp += 4       ; pop return address and jump to it

The following table would describe the function above:

code address .cfa r0 (on Google Code) r1 (on Google Code) ... .ra
func+0 sp cfa[0]
func+1 sp+16 cfa[0]
func+2 sp+16 cfa[-4] cfa[0]
func+11 sp+20 cfa[-4] cfa[0]
func+21 sp+20 cfa[0]
func+22 sp cfa[0]

Some things to note here:

  • Each row describes the state of affairs before executing the instruction
    at the given address. Thus, the row for func+0 describes the state before we
    execute the first instruction, which allocates the stack frame. In the next
    row, the formula for computing the CFA has changed, reflecting the
    allocation.

  • The other entries are written in terms of the CFA; this allows them to
    remain unchanged as the stack pointer gets bumped around. For example, to
    find the caller's value for r0 (on Google Code) at func+2, we would first
    compute the CFA by adding 16 to the sp, and then subtract four from that to
    find the address at which r0 (on Google Code) was saved.

  • Although the example doesn't show this, most calling conventions designate
    "callee-saves" and "caller-saves" registers. The callee must restore the
    values of "callee-saves" registers before returning (if it uses them at
    all), whereas the callee is free to use "caller-saves" registers without
    restoring their values. A function that uses caller-saves registers
    typically does not save their original values at all; in this case, the CFI
    marks such registers' values as "unrecoverable".

  • Exactly where the CFA points in the frame --- at the return address? below
    it? At some fixed point within the frame? --- is a question of definition
    that depends on the architecture and ABI in use. But by definition, the CFA
    remains constant throughout the lifetime of the frame. It's up to
    architecture- specific code to know what significance to assign the CFA, if
    any.

To save space, the most common type of CFI record only mentions the table
entries at which changes take place. So for the above, the CFI data would only
actually mention the non-blank entries here:

insn cfa r0 (on Google Code) r1 (on Google Code) ... ra
func+0 sp cfa[0]
func+1 sp+16
func+2 cfa[-4]
func+11 sp+20
func+21 r0 (on Google Code)
func+22 sp

A STACK CFI INIT record indicates that, at the machine instruction at
address, belonging to some function, the value that registern had
in that function's caller can be recovered by evaluating
expressionn. The values of any callee-saves registers not mentioned
are assumed to be unchanged. (STACK CFI records never mention caller-saves
registers.) These rules apply starting at address and continue up to, but not
including, the address given in the next STACK CFI record. The size field is
the total number of bytes of machine code covered by this record and any
subsequent STACK CFI records (until the next STACK CFI INIT record). The
address field is relative to the module's load address.

A STACK CFI record (no INIT) is the same, except that it mentions only those
registers whose recovery rules have changed from the previous CFI record. There
must be a prior STACK CFI INIT or STACK CFI record in the symbol file. The
address field of this record must be greater than that of the previous record,
and it must not be at or beyond the end of the range given by the most recent
STACK CFI INIT record. The address is relative to the module's load address.

Each expression is a breakpad-style postfix expression. Expressions may contain
spaces, but their tokens may not end with colons. When an expression mentions a
register, it refers to the value of that register in the callee, even if a prior
name/expression pair gives that register's value in the caller. The exception is
.cfa, which refers to the canonical frame address computed by the .cfa rule in
force at the current instruction.

The special expression .undef indicates that the given register's value cannot
be recovered.

The register names preceding the expressions are always followed by colons. The
expressions themselves never contain tokens ending with colons.

There are two special register names:

  • .cfa ("Canonical Frame Address") is the base address of the stack frame.
    Other registers' rules may refer to this. If no rule is provided for the
    stack pointer, the value of .cfa is the caller's stack pointer.

  • .ra is the return address. This is the value of the restored program
    counter. We use .ra instead of the architecture-specific name for the
    program counter.

The Breakpad stack walker requires that there be rules in force for .cfa and
.ra at every code address from which it unwinds. If those rules are not
present, the stack walker will ignore the STACK CFI data, and try to use a
different strategy.

So the CFI for the example function above would be as follows, if func were at
address 0x1000 (relative to the module's load address):

text
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

Thirdparty Sentry Native External Breakpad Docs Windows Client Integration (`thirdparty/sentry-native/external/breakpad/docs/windows_client_integration.md`)

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:

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


Thirdparty Sentry Native External Crashpad Doc Developing (`thirdparty/sentry-native/external/crashpad/doc/developing.md`)

Developing Crashpad

Status

Project status information has moved to its own page.

Introduction

Crashpad is a Chromium project. Most of its
development practices follow Chromium’s. In order to function on its own in
other projects, Crashpad uses
mini_chromium, a
small, self-contained library that provides many of Chromium’s useful low-level
base routines. mini_chromium’s
README

provides more detail.

Prerequisites

To develop Crashpad, the following tools are necessary, and must be present in
the $PATH environment variable:

  • Appropriate development tools.
    • On macOS, install Xcode. The latest
      version is generally recommended.
    • On Windows, install Visual Studio with
      C++ support and the Windows SDK. The latest version is generally
      recommended. Some tests also require the CDB debugger, installed with
      Debugging Tools for
      Windows
      .
    • On Linux, obtain appropriate tools for C++ development through any
      appropriate means including the system’s package manager. On Debian and
      Debian-based distributions, the build-essential, zlib1g-dev, and any
      one of the libcurl4-*-dev packages such as libcurl4-openssl-dev should
      suffice.
  • Chromium’s
    depot_tools.
  • Git. This is provided by Xcode on macOS, by
    depot_tools on Windows, and through any appropriate means including the
    system’s package manager on Linux.
  • Python. This is provided by the operating system
    on macOS, by depot_tools on Windows, and through any appropriate means
    including the system’s package manager on Linux.

Getting the Source Code

The main source code repository is a Git repository hosted at
https://chromium.googlesource.com/crashpad/crashpad. Although it is possible to
check out this repository directly with git clone, Crashpad’s dependencies are
managed by
gclient
instead of Git submodules, so to work on Crashpad, it is best to use fetch to
get the source code.

fetch and gclient are part of the
depot_tools. There’s
no need to install them separately.

Initial Checkout

text
$ mkdir ~/crashpad
$ cd ~/crashpad
$ fetch crashpad

fetch crashpad performs the initial git clone and gclient sync,
establishing a fully-functional local checkout.

Subsequent Checkouts

text
$ cd ~/crashpad/crashpad
$ git pull -r
$ gclient sync

Building

Windows, Mac, Linux, Fuchsia

On Windows, Mac, Linux, and Fuchsia, Crashpad uses
GN to generate
Ninja build files. For example,

text
$ cd ~/crashpad/crashpad
$ gn gen out/Default
$ ninja -C out/Default

You can then use gn args out/Default or edit out/Default/args.gn to
configure the build, for example things like is_debug=true or
target_cpu="x86".

GN and Ninja are part of the
depot_tools. There’s
no need to install them separately.

Fuchsia

In order to instruct gclient to download the Fuchsia SDK, you need to add the
following to ~/crashpad/.gclient:

text
target_os=["fuchsia"]

If you're using this tree to develop for multiple targets, you can also add
other entries to the the list (e.g. target_os=["fuchsia", "mac"]).

Optional Linux Configs

To pull and use Crashpad's version of clang and sysroot, make the following
changes.

Add the following to ~/crashpad/.gclient.

text
"custom_vars": { "pull_linux_clang": True },

Add these args to out/Default/args.gn.

text
clang_path = "//third_party/linux/clang/linux-amd64"
target_sysroot = "//third_party/linux/sysroot"

Android

This build relies on cross-compilation. It’s possible to develop Crashpad for
Android on any platform that the Android NDK (Native Development
Kit)
runs on.

If it’s not already present on your system, download the NDK package for your
system
and expand it to a
suitable location. These instructions assume that it’s been expanded to
~/android-ndk-r21b.

Note that Chrome uses Android API level 21 for both 64-bit platforms and
32-bit platforms. See Chrome’s
build/config/android/config.gni
which sets android32_ndk_api_level and android64_ndk_api_level.

Set these gn args

text
target_os = "android"
android_ndk_root = ~/android-ndk-r21b
android_api_level = 21

Testing

Crashpad uses Google Test as its
unit-testing framework, and some tests use Google
Mock
as well. Its
tests are currently split up into several test executables, each dedicated to
testing a different component. This may change in the future. After a successful
build, the test executables will be found at out/Debug/crashpad_*_test.

text
$ cd ~/crashpad/crashpad
$ out/Debug/crashpad_minidump_test
$ out/Debug/crashpad_util_test

A script is provided to run all of Crashpad’s tests. It accepts a single
argument, a path to the directory containing the test executables.

text
$ cd ~/crashpad/crashpad
$ python build/run_tests.py out/Debug

To run a subset of the tests, use the --gtest_filter flag, e.g., to run all
the tests for MinidumpStringWriter:

text
$ python build/run_tests.py out/Debug --gtest_filter MinidumpStringWriter\*

Windows

On Windows, end_to_end_test.py requires the CDB debugger, installed with
Debugging Tools for
Windows
. This
can be installed either as part of the Windows Driver
Kit
or
the Windows
SDK
. If the
Windows SDK has already been installed (possibly with Visual Studio) but
Debugging Tools for Windows is not present, it can be installed from Add or
remove programs→Windows Software Development Kit.

Android

To test on Android, ADB (Android Debug
Bridge)
from the
Android SDK must be in the PATH. Note
that it is sufficient to install just the command-line tools from the Android
SDK. The entire Android Studio IDE is not necessary to obtain ADB.

When asked to test an Android build directory, run_tests.py will detect a
single connected Android device (including an emulator). If multiple devices are
connected, one may be chosen explicitly with the ANDROID_DEVICE environment
variable. run_tests.py will upload test executables and data to a temporary
location on the detected or selected device, run them, and clean up after itself
when done.

Fuchsia

To test on Fuchsia, you need a connected device running Fuchsia. Run:

text
$ gn gen out/fuchsia --args 'target_os="fuchsia" target_cpu="x64" is_debug=true'
$ ninja -C out/fuchsia
$ python build/run_tests.py out/fuchsia

If you have multiple devices running, you will need to specify which device you
want using their hostname, for instance:

text
$ ZIRCON_NODENAME=scare-brook-skip-dried python build/run_tests.py out/fuchsia

Contributing

Crashpad’s contribution process is very similar to Chromium’s contribution
process
.

Code Review

A code review must be conducted for every change to Crashpad’s source code. Code
review is conducted on Chromium’s
Gerrit
system, and all code reviews
must be sent to an appropriate reviewer, with a Cc sent to
crashpad-dev. The
codereview.settings
file specifies this environment to git-cl.

git-cl is part of the
depot_tools. There’s
no need to install it separately.

text
$ cd ~/crashpad/crashpad
$ git checkout -b work_branch origin/main
…do some work…
$ git add …
$ git commit
$ git cl upload

Uploading a patch to Gerrit does not automatically request a review. You must
select a reviewer on the Gerrit review page after running git cl upload. This
action notifies your reviewer of the code review request. If you have lost track
of the review page, git cl issue will remind you of its URL. Alternatively,
you can request review when uploading to Gerrit by using git cl upload --send-mail.

Git branches maintain their association with Gerrit reviews, so if you need to
make changes based on review feedback, you can do so on the correct Git branch,
committing your changes locally with git commit. You can then upload a new
patch set with git cl upload and let your reviewer know you’ve addressed the
feedback.

The most recently uploaded patch set on a review may be tested on a
trybot
by running git cl try or by clicking the β€œCQ Dry Run” button in Gerrit. These
set the β€œCommit-Queue: +1” label. This does not mean that the patch will be
committed, but the trybot and commit queue share infrastructure and a Gerrit
label. The patch will be tested on trybots in a variety of configurations.
Status information will be available on Gerrit. Trybot access is available to
Crashpad and Chromium committers.

Landing Changes

After code review is complete and β€œCode-Review: +1” has been received from all
reviewers, the patch can be submitted to Crashpad’s commit
queue

by clicking the β€œSubmit to CQ” button in Gerrit. This sets the β€œCommit-Queue:
+2” label, which tests the patch on trybots before landing it. Commit queue
access is available to Crashpad and Chromium committers.

Although the commit queue is recommended, if needed, project members can bypass
the commit queue and land patches without testing by using the β€œSubmit” button
in Gerrit or by committing via git cl land:

text
$ cd ~/crashpad/crashpad
$ git checkout work_branch
$ git cl land

External Contributions

Copyright holders must complete the Individual Contributor License
Agreement
or
Corporate Contributor License
Agreement
as
appropriate before any submission can be accepted, and must be listed in the
AUTHORS
file. Contributors may be listed in the
CONTRIBUTORS
file.

Buildbot

The Crashpad Buildbot
performs automated builds and tests of Crashpad. Before checking out or updating
the Crashpad source code, and after checking in a new change, it is prudent to
check the Buildbot to ensure that β€œthe tree is green.”


Thirdparty Sentry Native External Crashpad Doc Ios Overview Design (`thirdparty/sentry-native/external/crashpad/doc/ios_overview_design.md`)

iOS Crashpad Overview Design

[TOC]

iOS Limitations

Crashpad on other platforms captures exceptions out-of-process. The iOS sandbox,
however, restricts applications from delegating work to separate processes.
This limitation means Crashpad on iOS must combine the work of the handler and
the client into the same process as the main application.

The Crashpad In-Process Handler

In-process handling comes with a number of limitations and difficulties. It is
not possible to catch the specific Mach exception EXC_CRASH, so certain groups
of crashes cannot be captured. This includes some major ones, like out-of-memory
crashes. This also introduces difficulties in capturing all the relevant crash
data and writing the minidump, as the process itself is in an unsafe state.

While handling an exception, the handler may not, for example:

  • Allocate memory.
  • Use libc, or most any library call.

While handling an exception, the handler may only:

  • Use audited syscalls.
  • access memory via vm_read.

In conjunction with Crashpad’s existing minidump writer and structural
limitations of the minidump format, it is not possible to write a minidump
immediately from the crash handler. Instead, an intermediate dump is written
when a handler would normally write a minidump (such as during an exception or a
forced dump without crashing). The intermediate dump file will be converted to
a minidump on the next run (or when the application decides it's safe to do so).

During Crashpad initialization, the handler gathers basic system information
and opens a pending intermediate dump adjacent to the Crashpad database.

The Crashpad IntermediateDump Format

Due to the limitations of in-process handling, an intermediate dump file is
written during exceptions. The data is streamed to a file, which will be used to
generate a final minidump when appropriate.

The file format is similar to binary JSON, supporting keyed properties, maps and
arrays.

  • Property [key:int, length:int, value:intarray]
  • StartMap [key:int], followed by repeating Properties until EndMap
  • StartArray [key:int], followed by repeating Maps until EndArray
  • EndMap, EndArray, EndDocument

Similar to JSON, maps can contain other maps, arrays and properties.

The life of an iOS crash report

Immediately upon calling StartCrashpadInProcessHandler, the iOS in-process
handler is installed. This will open a temporary file within the database
directory, in a subdirectory named pending-serialized-ios-dump. This file will
be used to write an intermediate dump in the event of a crash. This must happen
before installing the various types of crash handlers, as each depends on having
a valid handler with an intermediate dump ready to be written to.

After the in-process handler is initialized, the Mach exception, POSIX signal
and Objective-C exception preprocessor handlers are installed.

Intermediate Dump File Locking

It is expected that multiple Crashpad clients may share the same database
directory, and this directory may be inside an iOS app group directory. While
it's possible for each Crashpad client to write to its own private directory,
if a shared directory is used, it's possible for different applications to
upload a crash report from any application in a shared group. This might be
used, for example, by an application and its various app extensions, where each
client may generate a crash report but only the main application uploads
reports. Alternatively, a suite of applications may upload each other's crash
reports. Otherwise, the only opportunity to upload a report would be when a
specific app that crashed relaunches.

To prevent multiple clients from processing a pending intermediate dump, files
must be locked. However, POSIX locks on app group files will trigger app
termination on app backgrounding, so a custom file locking protocol is used.
Locked temporary files are named @.locked. The .locked
extension is removed when the file is unlocked. The bundle-id is used to
determine which Crashpad clients can process leftover locked files.

Writing Crashes to Intermediate Dumps

When an app encounters a crash (via a Mach exception, Objective-C exception, or
a POSIX signal), an intermediate dump is written to the temporary locked file,
the .locked extension is removed, and a new temporary locked file is opened.

App terminations not handled by Crashpad will leave behind a temporary
locked file, to be cleaned up on next launch. These files are still processed,
because it is possible for the app to be terminated while writing an
intermediate dump, and if enough data is written this may still be valuable.

Note: Generally iOS apps are single-process, so it's safe for the client to
consider any files matching its bundle-id, but there are edge-cases (such as
if a share-to app extension is opened at the same time in two different apps) so
old locked files won't be cleared until after 24 hours. Any locked file found
after 60 days is unlocked regardless of bundle-id.

Writing to Intermediate Dumps without a Crash

Apps may also generate intermediate dumps without a crash, often used for
debugging. Chromium makes heavy use of this for detecting main thread hangs,
something that can appear as a crash for the user, but is uncatchable for crash
handlers like Crashpad. When an app requests this (via DumpWithoutCrash,
DumpWithoutCrashAndDeferProcessing), an intermediate dump is written to the
temporary locked file, the .locked extension is removed, and a new temporary
locked file is opened.

Note: DumpWithoutCrashAndDeferProcessingAtPath writes an intermediate dump to
the requested location, not the previously opened temporary file. This is useful
because Chromium's main thread hang detection will throw away hang reports in
certain circumstances (if the app recovers, if a different crash report is
written, etc).

The Crashpad In-Process Client

Other Crashpad platforms handle exceptions and upload minidumps out-of-process.
On iOS, everything must happen in-process. Once started, the client will
automatically handle exceptions and capture the crashed process state in an
intermediate dump file. Converting that intermediate dump file into a minidump
is likely not safe to do from within a crashed process, and uploading a minidump
is definitely unsafe to do at crash time. Applications are expected to process
intermediate dumps into pending minidumps and begin processing pending
minidumps, possibly for upload, at suitable times following the next application
restart.

Note: Applications are not required to call either of these methods. For
example, application extensions may choose to generate dumps but leave
processing and uploading to the main applications. Clients that share the
same database directory between apps can take advantage of processing and
uploading crash reports from different applications.

`ProcessIntermediateDumps`

For performance and stability reasons applications may choose the correct time
to convert intermediate dumps, as well as append metadata to the pending
intermediate dumps. This is expected to happen during application startup, when
suitable. After converting, a minidump will be written to the Crashpad database,
similar to how other platforms write a minidump on exception handling. If
uploading is enabled, this minidump will also be immediately uploaded. New
intermediate dumps generated by exceptions or by
CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING will not be processed until
the next call to ProcessIntermediateDumps. Conversely,
CRASHPAD_SIMULATE_CRASH can be called when the client has no performance or
stability concerns. In this case, intermediate dumps are automatically
converted to minidumps and immediately eligible for uploading.

Applications can include annotations here as well. Chromium uses this for its
insta-crash logic, which detects if an app is crashing repeatedly on startup.

`StartProcessingPendingReports`

For similar reasons, applications may choose the correct time to begin uploading
pending reports, such as when ideal network conditions exist. By default,
clients start with uploading disabled. Applications should call this API when
it is determined that it is appropriate to do so (such as on a few seconds after
startup, or when network connectivity is appropriate).

tvOS considerations

tvOS, an operating system based on iOS, shares the constraints described above
and uses the same architecture. Additionally, tvOS does not allow the use of
the mach_msg() APIs so using Mach exceptions is impossible.

Consequently, the Mach exception handler is not installed once the in-process
handler is initialized; the tvOS handler only uses the POSIX signals and
Objective-C exception preprocessors. Furthermore, the POSIX signal handler is
unable to handle all crashes that the Mach exception handler can (e.g. stack
overflows, as sigaltstack() cannot be used either). In other words, the tvOS
in-process handler can only handle a subset of all exceptions that the iOS
in-process handler does.


Thirdparty Sentry Native External Crashpad Doc Man (`thirdparty/sentry-native/external/crashpad/doc/man.md`)

Man Pages

Section 1: User Commands

macOS-Specific

Section 8: DΣ•mons


Thirdparty Sentry Native External Crashpad Doc Overview Design (`thirdparty/sentry-native/external/crashpad/doc/overview_design.md`)

Crashpad Overview Design

[TOC]

Objective

Crashpad is a library for capturing, storing and transmitting postmortem crash
reports from a client to an upstream collection server. Crashpad aims to make it
possible for clients to capture process state at the time of crash with the best
possible fidelity and coverage, with the minimum of fuss.

Crashpad also provides a facility for clients to capture dumps of process state
on-demand for diagnostic purposes.

Crashpad additionally provides minimal facilities for clients to adorn their
crashes with application-specific metadata in the form of per-process key/value
pairs. More sophisticated clients are able to adorn crash reports further
through extensibility points that allow the embedder to augment the crash report
with application-specific metadata.

Background

It’s an unfortunate truth that any large piece of software will contain bugs
that will cause it to occasionally crash. Even in the absence of bugs, software
incompatibilities can cause program instability.

Fixing bugs and incompatibilities in client software that ships to millions of
users around the world is a daunting task. User reports and manual reproduction
of crashes can work, but even given a user report, often times the problem is
not readily reproducible. This is for various reasons, such as e.g. system
version or third-party software incompatibility, or the problem can happen due
to a race of some sort. Users are also unlikely to report problems they
encounter, and user reports are often of poor quality, as unfortunately most
users don’t have experience with making good bug reports.

Automatic crash telemetry has been the best solution to the problem so far, as
this relieves the burden of manual reporting from users, while capturing the
hardware and software state at the time of crash.

TODO(siggi): examples of this?

Crash telemetry involves capturing postmortem crash dumps and transmitting them
to a backend collection server. On the server they can be stackwalked and
symbolized, and evaluated and aggregated in various ways. Stackwalking and
symbolizing the reports on an upstream server has several benefits over
performing these tasks on the client. High-fidelity stackwalking requires access
to bulky unwind data, and it may be desirable to not ship this to end users out
of concern for the application size. The process of symbolization requires
access to debugging symbols, which can be quite large, and the symbolization
process can consume considerable other resources. Transmitting un-stackwalked
and un-symbolized postmortem dumps to the collection server also allows deep
analysis of individual dumps, which is often necessary to resolve the bug
causing the crash.

Transmitting reports to the collection server allows aggregating crashes by
cause, which in turn allows assessing the importance of different crashes in
terms of the occurrence rate and e.g. the potential security impact.

A postmortem crash dump must contain the program state at the time of crash
with sufficient fidelity to allow diagnosing and fixing the problem. As the full
program state is usually too large to transmit to an upstream server, the
postmortem dump captures a heuristic subset of the full state.

The crashed program is in an indeterminate state and, in fact, has often crashed
because of corrupt global state - such as heap. It’s therefore important to
generate crash reports with as little execution in the crashed process as
possible. Different operating systems vary in the facilities they provide for
this.

Overview

Crashpad is a client-side library that focuses on capturing machine and program
state in a postmortem crash report, and transmitting this report to a backend
server - a β€œcollection server”. The Crashpad library is embedded by the client
application. Conceptually, Crashpad breaks down into the handler and the client.
The handler runs in a separate process from the client or clients. It is
responsible for snapshotting the crashing client process’ state on a crash,
saving it to a crash dump, and transmitting the crash dump to an upstream
server. Clients register with the handler to allow it to capture and upload
their crashes. On iOS, there is no separate process for the handler.
This is a limitation of iOS.

The Crashpad handler

The Crashpad handler is instantiated in a process supplied by the embedding
application. It provides means for clients to register themselves by some means
of IPC, or where operating system support is available, by taking advantage of
such support to cause crash notifications to be delivered to the handler. On
crash, the handler snapshots the crashed client process’ state, writes it to a
postmortem dump in a database, and may also transmit the dump to an upstream
server if so configured.

The Crashpad handler is able to handle cross-bitted requests and generate crash
dumps across bitness, where e.g. the handler is a 64-bit process while the
client is a 32-bit process or vice versa. In the case of Windows, this is
limited by the OS such that a 32-bit handler can only generate crash dumps for
32-bit clients, but a 64-bit handler can acquire nearly all of the detail for a
32-bit process.

The Crashpad client

The Crashpad client provides two main facilities.

  1. Registration with the Crashpad handler.
  2. Metadata communication to the Crashpad handler on crash.

A Crashpad embedder links the Crashpad client library into one or more
executables, whether a loadable library or a program file. The client process
then registers with the Crashpad handler through some mode of IPC or other
operating system-specific support.

On crash, metadata is communicated to the Crashpad handler via the CrashpadInfo
structure. Each client executable module linking the Crashpad client library
embeds a CrashpadInfo structure, which can be updated by the client with
whatever state the client wishes to record with a crash.

Here is an overview picture of the conceptual relationships between embedder (in
light blue), client modules (darker blue), and Crashpad (in green). Note that
multiple client modules can contain a CrashpadInfo structure, but only one
registration is necessary.

Detailed Design

Requirements

The purpose of Crashpad is to capture machine, OS and application state in
sufficient detail and fidelity to allow developers to diagnose and, where
possible, fix the issue causing the crash.

Each distinct crash report is assigned a globally unique ID, in order to allow
users to associate them with a user report, report in bug reports and so on.

It’s critical to safeguard the user’s privacy by ensuring that no crash report
is ever uploaded without user consent. Likewise it’s important to ensure that
Crashpad never captures or uploads reports from non-client processes.

Concepts

  • Client ID. A UUID tied to a single instance of a Crashpad database. When
    creating a crash report, the Crashpad handler includes the client ID stored
    in the database. This provides a means to determine how many individual end
    users are affected by a specific crash signature.

  • Crash ID. A UUID representing a single crash report. Uploaded crash
    reports also receive a β€œserver ID.” The Crashpad database indexes both the
    locally-generated and server-generated IDs.

  • Collection Server. See crash server documentation.

  • Client Process. Any process that has registered with a Crashpad handler.

  • Handler process. A process hosting the Crashpad handler library. This may
    be a dedicated executable, or it may be hosted within a client executable
    with control passed to it based on special signaling under the client’s
    control, such as a command-line parameter.

  • CrashpadInfo. A structure used by client modules to provide information to
    the handler.

  • Annotations. Each CrashpadInfo structure points to a dictionary of
    {string, string} annotations that the client can use to communicate
    application state in the case of crash.

  • Database. The Crashpad database contains persistent client settings as
    well as crash dumps pending upload.

TODO(siggi): moar concepts?

Overview Picture

Here is a rough overview picture of the various Crashpad constructs, their
layering and intended use by clients.

Dark blue boxes are interfaces, light blue boxes are implementation. Gray is the
embedding client application. Note that wherever possible, implementation that
necessarily has to be OS-specific, exposes OS-agnostic interfaces to the rest of
Crashpad and the client.

Registration

The particulars of how a client registers with the handler varies across
operating systems.

macOS

At registration time, the client designates a Mach port monitored by the
Crashpad handler as the EXC_CRASH exception port for the client. The port may be
acquired by launching a new handler process or by retrieving service already
registered with the system. The registration is maintained by the kernel and is
inherited by subprocesses at creation time by default, so only the topmost
process of a process tree need register.

Crashpad provides a facility for a process to disassociate (unregister) with an
existing crash handler, which can be necessary when an older client spawns an
updated version.

iOS

iOS registers both a signal handler for SIGABRT and a Mach exception handler
with a subset of available exceptions. This is a limitation of
iOS.

Windows

There are two modes of registration on Windows. In both cases the handler is
advised of the address of a set of structures in the client process’ address
space. These structures include a pair of ExceptionInformation structs, one for
generating a postmortem dump for a crashing process, and another one for
generating a dump for a non- crashing process.

Normal registration

In the normal registration mode, the client connects to a named pipe by a
pre-arranged name. A registration request is written to the pipe. During
registration, the handler creates a set of events, duplicates them to the
registering client, then returns the handle values in the registration response.
This is a blocking process.

Initial Handler Creation

In order to avoid blocking client startup for the creation and initialization of
the handler, a different mode of registration can be used for the handler
creation. In this mode, the client creates a set of event handles and inherits
them into the newly created handler process. The handler process is advised of
the handle values and the location of the ExceptionInformation structures by way
of command line arguments in this mode.

Linux/Android

On Linux, a registration is a connected socket pair between a client process and
the Crashpad handler. This socket pair may be private or shared among many
client processes.

Private Connections

Private connections are the default registration mode when starting the handler
process in response to a crash or on behalf of another client. This mode is
required to use a ptrace broker, which is in turn required to trace Android
isolated processes.

Shared Connections

Shared connections are the default mode when using a long-lived handler. The
same connected socket pair may be shared among any number of clients. The socket
pair is created by the first process to start the handler at which point the
client socket end may be shared with other clients by any convenient means (e.g.
inheritance).

Capturing Exceptions

The details of how Crashpad captures the exceptions leading to crashes varies
between operating systems.

macOS

On macOS, the operating system will notify the handler of client crashes via the
Mach port set as the client process’ exception port. As exceptions are
dispatched to the Mach port by the kernel, on macOS, exceptions can be handled
entirely from the Crashpad handler without the need to run any code in the crash
process at the time of the exception.

iOS

On iOS, the operating system will notify the handler of crashes via the Mach
exception port or the signal handler. As exceptions are handled in-process, an
intermediate dump file is generated rather than a minidump. See more
information about the iOS in-process
handler.

Windows

On Windows, the OS dispatches exceptions in the context of the crashing thread.
To notify the handler of exceptions, the Crashpad client registers an
UnhandledExceptionFilter (UEF) in the client process. When an exception trickles
up to the UEF, it stores the exception information and the crashing thread’s ID
in the ExceptionInformation structure registered with the handler. It then sets
an event handle to signal the handler to go ahead and process the exception.

Caveats

  • If the crashing thread’s stack is smashed when an exception occurs, the
    exception cannot be dispatched. In this case the OS will summarily terminate
    the process, without the handler having an opportunity to generate a crash
    report.
  • If an exception is handled in the crashing thread, it will never propagate
    to the UEF, and thus a crash report won’t be generated. This happens a fair
    bit in Windows as system libraries will often dispatch callbacks under a
    structured exception handler. This occurs during Window message dispatching
    on some system configurations, as well as during e.g. DLL entry point
    notifications.
  • A growing number of conditions in the system and runtime exist where
    detected corruption or illegal calls result in summary termination of the
    process, in which case no crash report will be generated.

Out-Of-Process Exception Handling

There exists a mechanism in Windows Error Reporting (WER) that allows a client
process to register for handling client exceptions out of the crashing process.
Unfortunately this mechanism is difficult to use, and doesn’t provide coverage
for many of the caveats above. Details
here.

Linux/Android

On Linux, exceptions are dispatched as signals to the crashing thread. Crashpad
signal handlers will send a message over the socket to the Crashpad handler
notifying it of the crash and the location of exception information to be read
from the crashing process. When using a shared socket connection, communication
is entirely one-way. The client sends its dump request to the handler and then
waits until the handler responds with a SIGCONT or a timeout occurs. When using
a private socket connection, the handler may respond over the socket to
communicate with a ptrace broker process. The broker is forked from the crashing
process, executes ptrace requests against the crashing process, and sends the
information over the socket to the handler.

The CrashpadInfo structure

The CrashpadInfo structure is used to communicate information from the client to
the handler. Each executable module in a client process can contain a
CrashpadInfo structure. On a crash, the handler crawls all modules in the
crashing process to locate all CrashpadInfo structures present. The CrashpadInfo
structures are linked into a special, named section of the executable, where the
handler can readily find them.

The CrashpadInfo structure has a magic signature, and contains a size and a
version field. The intent is to allow backwards compatibility from older client
modules to newer handler. It may also be necessary to provide forwards
compatibility from newer clients to older handler, though this hasn’t occurred
yet.

The CrashpadInfo structure contains such properties as the cap for how much
memory to include in the crash dump, some tristate flags for controlling the
handler’s behavior, a pointer to an annotation dictionary and so on.

Snapshot

Snapshot is a layer of interfaces that represent the machine and OS entities
that Crashpad cares about. Different concrete implementations of snapshot can
then be backed different ways, such as e.g. from the in-memory representation of
a crashed process, or e.g. from the contents of a minidump.

Crash Dump Creation

To create a crash dump, a subset of the machine, OS and application state is
grabbed from the crashed process into an in-memory snapshot structure in the
handler process. Since the full application state is typically too large for
capturing to disk and transmitting to an upstream server, the snapshot contains
a heuristically selected subset of the full state.

The precise details of what’s captured varies between operating systems, but
generally includes the following

  • The set of modules (executable, shared libraries) that are loaded into the
    crashing process.
  • An enumeration of the threads running in the crashing process, including the
    register contents and the contents of stack memory of each thread.
  • A selection of the OS-related state of the process, such as e.g. the command
    line, environment and so on.
  • A selection of memory potentially referenced from registers and from stack.

To capture a crash dump, the crashing process is first suspended, then a
snapshot is created in the handler process. The snapshot includes the
CrashpadInfo structures of the modules loaded into the process, and the contents
of those is used to control the level of detail captured for the crash dump.

Once the snapshot has been constructed, it is then written to a minidump file,
which is added to the database. The process is un-suspended after the minidump
file has been written. In the case of a crash (as opposed to a client request to
produce a dump without crashing), it is then either killed by the operating
system or the Crashpad handler.

In general the snapshotting process has to be very intimate with the operating
system it’s working with, so there will be a set of concrete implementation
classes, many deriving from the snapshot interfaces, doing this for each
operating system.

Minidump

The minidump implementation is responsible for writing a snapshot to a
serialized on-disk file in the minidump format. The minidump implementation is
OS-agnostic, as it works on an OS-agnostic Snapshot interface.

TODO(siggi): Talk about two-phase writes and contents ordering here.

Database

The Crashpad database contains persistent client settings, including a unique
crash client identifier and the upload-enabled bit. Note that the crash client
identifier is assigned by Crashpad, and is distinct from any identifiers the
client application uses to identify users, installs, machines or such - if any.
The expectation is that the client application will manage the user’s upload
consent, and inform Crashpad of changes in consent.

The unique client identifier is set at the time of database creation. It is then
recorded into every crash report collected by the handler and communicated to
the upstream server.

The database stores a configurable number of recorded crash dumps to a
configurable maximum aggregate size. For each crash dump it stores annotations
relating to whether the crash dumps have been uploaded. For successfully
uploaded crash dumps it also stores their server-assigned ID.

The database consists of a settings file, named "settings.dat" with binary
contents (see crashpad::Settings::Data for the file format), as well as
directory containing the crash dumps. Additionally each crash dump is adorned
with properties relating to the state of the dump for upload and such. The
details of how these properties are stored vary between platforms.

macOS

The macOS implementation simply stores database properties on the minidump files
in filesystem extended attributes.

iOS

The iOS implementation also stores database properties of minidump files in
filesystem extended attributes. Separate from the database, iOS also stores its
intermediate dump files adjacent to the database. See more information about
iOS intermediate
dumps.

Windows

The Windows implementation stores database properties in a binary file named
β€œmetadata” at the top level of the database directory.

Report Format

Crash reports are recorded in the Windows minidump format with
extensions to support Crashpad additions, such as e.g. Annotations.

Upload to collection server

Wire Format

For the time being, Crashpad uses the Breakpad wire protocol, which is
essentially a MIME multipart message communicated over HTTP(S). To support this,
the annotations from all the CrashpadInfo structures found in the crashing
process are merged to create the Breakpad β€œcrash keys” as form data. The
postmortem minidump is then attached as an β€œapplication/octet- stream”
attachment with the name β€œupload_file_minidump”. The entirety of the request
body, including the minidump, can be gzip-compressed to reduce transmission time
and increase transmission reliability. Note that by convention there is a set of
β€œcrash keys” that are used to communicate the product, version, client ID and
other relevant data about the client, to the server. Crashpad normally stores
these values in the minidump file itself, but retrieves them from the minidump
and supplies them as form data for compatibility with the Breakpad-style server.

This is a temporary compatibility measure to allow the current Breakpad-based
upstream server to handle Crashpad reports. In the fullness of time, the wire
protocol is expected to change to remove this redundant transmission and
processing of the Annotations.

Transport

The embedding client controls the URL of the collection server by the command
line passed to the handler. The handler can upload crashes with HTTP or HTTPS,
depending on client’s preference. It’s strongly suggested use HTTPS transport
for crash uploads to protect the user’s privacy against man-in-the-middle
snoopers.

TODO(mmentovai): Certificate pinning.

Throttling & Retry Strategy

To protect both the collection server from DDoS as well as to protect the
clients from unreasonable data transfer demands, the handler implements a
client-side throttling strategy. At the moment, the strategy is very simplistic,
it simply limits uploads to one upload per hour, and failed uploads are aborted.

An experiment has been conducted to lift all throttling. Analysis on the
aggregate data this produced shows that multiple crashes within a short timespan
on the same client are nearly always due to the same cause. Therefore there is
very little loss of signal due to the throttling, though the ability to
reconstruct at least the full crash count is highly desirable.

The lack of retry is expected to change
soon
, as this creates blind spots for
client crashes that exclusively occur on e.g. network down events, during
suspend and resume and such.

Extensibility

Client Extensibility

Clients are able to extend the generated crash reports in two ways, by
manipulating their CrashpadInfo structure.
The two extensibility points are:

  1. Nominating a set of address ranges for inclusion in the crash report.
  2. Adding user-defined minidump streams for inclusion in the crash report.

In both cases the CrashpadInfo structure has to be updated before a crash
occurs.

Embedder Extensibility

Additionally, embedders of the handler can provide "user stream data source"
instances to the handler's main function. Any time a minidump is written, these
instances get called.

Each data source may contribute a custom stream to the minidump, which can be
computed from e.g. system or application state relevant to the crash.

As a case in point, it can be handy to know whether the system was under memory
or other resource duress at the time of crash.

Dependencies

Aside from system headers and APIs, when used outside of Chromium, Crashpad has
a dependency on β€œmini_chromium”, which is a subset of the Chromium base library.
This is to allow non-Chromium clients to use Crashpad, without taking a direct
dependency on the Chromium base, while allowing Chromium projects to use
Crashpad with minimum code duplication or hassle. When using Crashpad as part of
Chromium, Chromium’s own copy of the base library is used instead of
mini_chromium.

The downside to this is that mini_chromium must be kept up to date with
interface and implementation changes in Chromium base, for the subset of
functionality used by Crashpad.

Caveats

TODO(anyone): You may need to describe what you did not do or why simpler
approaches don't work. Mention other things to watch out for (if any).

Security Considerations

Crashpad may be used to capture the state of sandboxed processes and it writes
minidumps to disk. It may therefore straddle security boundaries, so it’s
important that Crashpad handle all data it reads out of the crashed process with
extreme care. The Crashpad handler takes care to access client address spaces
through specially-designed accessors that check pointer validity and enforce
accesses within prescribed bounds. The flow of information into the Crashpad
handler is exclusively one-way: Crashpad never communicates anything back to
its clients, aside from providing single-bit indications of completion.

Privacy Considerations

Crashpad may capture arbitrary contents from crashed process’ memory, including
user IDs and passwords, credit card information, URLs and whatever other content
users have trusted the crashing program with. The client program must acquire
and honor the user’s consent to upload crash reports, and appropriately manage
the upload state in Crashpad’s database.

Crashpad must also be careful not to upload crashes for arbitrary processes on
the user’s system. To this end, Crashpad will never upload a process that hasn’t
registered with the handler, but note that registrations are inherited by child
processes on some operating systems.


Thirdparty Sentry Native External Crashpad Doc Status (`thirdparty/sentry-native/external/crashpad/doc/status.md`)

Project Status

Completed

Crashpad has complete crash-reporting clients and some related tools for macOS,
Windows, Fuchsia, and Linux (including Android and Chromium OS). Crashpad became
the crash reporter client for Chromium on macOS
as of March
2015
,
Windows as of November
2015
,
and Android as of January
2019
.

In Progress

Chromium is transitioning to Crashpad for Chromium OS and Desktop
Linux
.

Work has begun on a Crashpad client for
iOS.

Future

There are also plans to implement a crash report
processor
as part of Crashpad. No
timeline for completing this work has been set yet.


Thirdparty Sentry Native External Crashpad Libunwind Docs BuildingLibunwind (`thirdparty/sentry-native/external/crashpad/libunwind/docs/BuildingLibunwind.rst`)

.. _BuildingLibunwind:

================== Building libunwind

.. contents::
:local:

.. _build instructions:

Getting Started

On Mac OS, the easiest way to get this library is to link with -lSystem.
However if you want to build tip-of-trunk from here (getting the bleeding
edge), read on.

The basic steps needed to build libc++ are:

#. Checkout LLVM, libunwind, and related projects:

  • cd where-you-want-llvm-to-live
  • git clone https://github.com/llvm/llvm-project.git

#. Configure and build libunwind:

CMake is the only supported configuration system.

Clang is the preferred compiler when building and using libunwind.

  • cd where you want to build llvm
  • mkdir build
  • cd build
  • cmake -G <generator> -DLLVM_ENABLE_PROJECTS=libunwind [options]

For more information about configuring libunwind see :ref:CMake Options.

  • make unwind --- will build libunwind.
  • make check-unwind --- will run the test suite.

Shared and static libraries for libunwind should now be present in llvm/build/lib.

#. Optional: Install libunwind

If your system already provides an unwinder, it is important to be careful
not to replace it. Remember Use the CMake option CMAKE_INSTALL_PREFIX to
select a safe place to install libunwind.

  • make install-unwind --- Will install the libraries and the headers

It is sometimes beneficial to build outside of the LLVM tree. An out-of-tree
build would look like this:

.. code-block:: bash

$ cd where-you-want-libunwind-to-live
$ # Check out llvm, and libunwind
$ svn co https://llvm.org/svn/llvm-project/llvm/trunk llvm
$ svn co https://llvm.org/svn/llvm-project/libunwind/trunk libunwind
$ cd where-you-want-to-build
$ mkdir build && cd build
$ export CC=clang CXX=clang++
$ cmake -DLLVM_PATH=path/to/llvm
path/to/libunwind
$ make

.. _CMake Options:

CMake Options

Here are some of the CMake variables that are used often, along with a
brief explanation and LLVM-specific notes. For full documentation, check the
CMake docs or execute cmake --help-variable VARIABLE_NAME.

CMAKE_BUILD_TYPE:STRING
Sets the build type for make based generators. Possible values are
Release, Debug, RelWithDebInfo and MinSizeRel. On systems like Visual Studio
the user sets the build type with the IDE settings.

CMAKE_INSTALL_PREFIX:PATH
Path where LLVM will be installed if "make install" is invoked or the
"INSTALL" target is built.

CMAKE_CXX_COMPILER:STRING
The C++ compiler to use when building and testing libunwind.

.. _libunwind-specific options:

libunwind specific options

.. option:: LIBUNWIND_BUILD_32_BITS:BOOL

Default: Same as LLVM_BUILD_32_BITS

Toggle whether libunwind should be built with -m32.

.. option:: LIBUNWIND_ENABLE_ASSERTIONS:BOOL

Default: ON

Toggle assertions independent of the build mode.

.. option:: LIBUNWIND_ENABLE_PEDANTIC:BOOL

Default: ON

Compile with -Wpedantic.

.. option:: LIBUNWIND_ENABLE_WERROR:BOOL

Default: ON

Compile with -Werror

.. option:: LIBUNWIND_ENABLE_SHARED:BOOL

Default: ON

Build libunwind as a shared library.

.. option:: LIBUNWIND_ENABLE_STATIC:BOOL

Default: ON

Build libunwind as a static archive.

.. option:: LIBUNWIND_ENABLE_CROSS_UNWINDING:BOOL

Default: OFF

Enable cross-platform unwinding support.

.. option:: LIBUNWIND_ENABLE_ARM_WMMX:BOOL

Default: OFF

Enable unwinding support for ARM WMMX registers.

.. option:: LIBUNWIND_ENABLE_THREADS:BOOL

Default: ON

Build libunwind with threading support.

.. option:: LIBUNWIND_TARGET_TRIPLE:STRING

Target triple for cross compiling

.. option:: LIBUNWIND_GCC_TOOLCHAIN:PATH

GCC toolchain for cross compiling

.. option:: LIBUNWIND_SYSROOT

Sysroot for cross compiling

.. option:: LIBUNWIND_INSTALL_LIBRARY_DIR:PATH

Default: lib${LIBUNWIND_LIBDIR_SUFFIX}

Path where built libunwind libraries should be installed. If a relative path,
relative to CMAKE_INSTALL_PREFIX.


Thirdparty Sentry Native External Crashpad Libunwind Docs CMakeLists (`thirdparty/sentry-native/external/crashpad/libunwind/docs/CMakeLists.txt`)

include(FindSphinx)
if (SPHINX_FOUND AND LLVM_ENABLE_SPHINX)
include(AddSphinxTarget)
if (${SPHINX_OUTPUT_HTML})
add_sphinx_target(html libunwind)
endif()
endif()


Thirdparty Sentry Native External Crashpad Libunwind Docs README (`thirdparty/sentry-native/external/crashpad/libunwind/docs/README.txt`)

libunwind Documentation

The libunwind documentation is written using the Sphinx documentation generator. It is
currently tested with Sphinx 1.1.3.

To build the documents into html configure libunwind with the following cmake options:

  • -DLLVM_ENABLE_SPHINX=ON
  • -DLIBUNWIND_INCLUDE_DOCS=ON

After configuring libunwind with these options the make rule docs-libunwind-html
should be available.


Thirdparty Sentry Native External Crashpad Libunwind Docs Index (`thirdparty/sentry-native/external/crashpad/libunwind/docs/index.rst`)

.. _index:

======================= libunwind LLVM Unwinder

Overview

libunwind is an implementation of the interface defined by the HP libunwind
project. It was contributed by Apple as a way to enable clang++ to port to
platforms that do not have a system unwinder. It is intended to be a small and
fast implementation of the ABI, leaving off some features of HP's libunwind
that never materialized (e.g. remote unwinding).

The unwinder has two levels of API. The high level APIs are the _Unwind_*
functions which implement functionality required by __cxa_* exception
functions. The low level APIs are the unw_* functions which are an interface
defined by the old HP libunwind project.

Getting Started with libunwind

.. toctree::
:maxdepth: 2

BuildingLibunwind

Current Status

libunwind is a production-quality unwinder, with platform support for DWARF
unwind info, SjLj, and ARM EHABI.

The low level libunwind API was designed to work either in-process (aka local)
or to operate on another process (aka remote), but only the local path has been
implemented. Remote unwinding remains as future work.

Platform and Compiler Support

libunwind is known to work on the following platforms:

============ ======================== ============ ========================
OS Arch Compilers Unwind Info
============ ======================== ============ ========================
Any i386, x86_64, ARM Clang SjLj
Bare Metal ARM Clang, GCC EHABI
FreeBSD i386, x86_64, ARM64 Clang DWARF CFI
iOS ARM Clang SjLj
Linux ARM Clang, GCC EHABI
Linux i386, x86_64, ARM64 Clang, GCC DWARF CFI
macOS i386, x86_64 Clang, GCC DWARF CFI
NetBSD x86_64 Clang, GCC DWARF CFI
Windows i386, x86_64, ARM, ARM64 Clang DWARF CFI
============ ======================== ============ ========================

The following minimum compiler versions are strongly recommended.

  • Clang 3.5 and above
  • GCC 4.7 and above.

Anything older may work.

Notes and Known Issues

  • TODO

Getting Involved

First please review our Developer's Policy <https://llvm.org/docs/DeveloperPolicy.html>__
and Getting started with LLVM <https://llvm.org/docs/GettingStarted.html>__.

Bug Reports

If you think you've found a bug in libunwind, please report it using
the LLVM Bugzilla. If you're not sure, you
can post a message to the cfe-dev mailing list
or on IRC.
Please include "libunwind" in your subject.

Patches

If you want to contribute a patch to libunwind, the best place for that is
Phabricator <https://llvm.org/docs/Phabricator.html>. Please include [libunwind] in the subject and
add cfe-commits as a subscriber. Also make sure you are subscribed to the
cfe-commits mailing list <http://lists.llvm.org/mailman/listinfo/cfe-commits>
.

Discussion and Questions

Send discussions and questions to the
cfe-dev mailing list <http://lists.llvm.org/mailman/listinfo/cfe-dev>_.
Please include [libunwind] in the subject.

  • LLVM Homepage <https://llvm.org/>_
  • LLVM Bugzilla <https://bugs.llvm.org/>_
  • cfe-commits Mailing List_
  • cfe-dev Mailing List_
  • Browse libunwind Sources <https://github.com/llvm/llvm-project/blob/main/libunwind/>_

--- METRICS ---