Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3a642b92dc | ||
![]() |
cc8194139c |
83
.gitignore
vendored
83
.gitignore
vendored
@ -1,83 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Ignore other VCSs.
|
||||
.svn/
|
||||
|
||||
# Ignore common compiled artifacts.
|
||||
*~
|
||||
*.dwo
|
||||
*.o
|
||||
lib*.a
|
||||
/breakpad.pc
|
||||
/breakpad-client.pc
|
||||
/src/client/linux/linux_client_unittest_shlib
|
||||
/src/client/linux/linux_dumper_unittest_helper
|
||||
/src/processor/microdump_stackwalk
|
||||
/src/processor/minidump_dump
|
||||
/src/processor/minidump_stackwalk
|
||||
/src/tools/linux/core2md/core2md
|
||||
/src/tools/linux/dump_syms/dump_syms
|
||||
/src/tools/linux/md2core/minidump-2-core
|
||||
/src/tools/linux/symupload/minidump_upload
|
||||
/src/tools/linux/symupload/sym_upload
|
||||
/src/tools/mac/dump_syms/dump_syms
|
||||
|
||||
# Ignore autotools generated artifacts.
|
||||
.deps
|
||||
.dirstamp
|
||||
autom4te.cache/
|
||||
/config.cache
|
||||
config.h
|
||||
/config.log
|
||||
/config.status
|
||||
/Makefile
|
||||
stamp-h1
|
||||
|
||||
# Ignore GYP generated Visual Studio artifacts.
|
||||
*.filters
|
||||
*.sdf
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*.vcxproj
|
||||
|
||||
# Ignore GYP generated Makefiles
|
||||
src/Makefile
|
||||
*.Makefile
|
||||
*.target.mk
|
||||
|
||||
# Ignore compiled Python files.
|
||||
*.pyc
|
||||
|
||||
# Ignore directories gclient syncs.
|
||||
src/testing
|
||||
src/third_party/glog
|
||||
src/third_party/lss
|
||||
src/third_party/protobuf
|
||||
src/tools/gyp
|
26
.travis.yml
26
.travis.yml
@ -1,26 +0,0 @@
|
||||
# Travis build integration.
|
||||
# https://docs.travis-ci.com/
|
||||
language: cpp
|
||||
# TODO: add a clang build as well.
|
||||
compiler:
|
||||
- gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gcc-4.8
|
||||
- g++-4.8
|
||||
# Travis sets CC/CXX to the system toolchain based on the `compiler`
|
||||
# selection. If clang is added, this should move to be set inside the
|
||||
# matrix.
|
||||
env:
|
||||
- USE_CC=gcc-4.8 USE_CXX=g++-4.8
|
||||
before_install: ./scripts/travis-checkout.sh
|
||||
script: ./scripts/travis-build.sh
|
||||
# TODO: add mac support
|
||||
os:
|
||||
- linux
|
||||
notifications:
|
||||
email:
|
||||
- google-breakpad-dev@googlegroups.com
|
@ -26,25 +26,3 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Copyright 2001-2004 Unicode, Inc.
|
||||
|
||||
Disclaimer
|
||||
|
||||
This source code is provided as is by Unicode, Inc. No claims are
|
||||
made as to fitness for any particular purpose. No warranties of any
|
||||
kind are expressed or implied. The recipient agrees to determine
|
||||
applicability of information provided. If this file has been
|
||||
purchased on magnetic or optical media from Unicode, Inc., the
|
||||
sole remedy for any claim will be exchange of defective media
|
||||
within 90 days of receipt.
|
||||
|
||||
Limitations on Rights to Redistribute This Code
|
||||
|
||||
Unicode, Inc. hereby grants the right to freely use the information
|
||||
supplied in this file in the creation of products supporting the
|
||||
Unicode Standard, and to make copies of this file in any form
|
||||
for internal or external distribution as long as this notice
|
||||
remains attached.
|
51
DEPS
51
DEPS
@ -1,4 +1,5 @@
|
||||
# Copyright 2010 Google Inc. All rights reserved.
|
||||
# Copyright (c) 2010, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
@ -26,50 +27,18 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This is used to mimic the svn:externals mechanism for gclient (both Git and
|
||||
# SVN) based checkouts of Breakpad. As such, its use is entirely optional. If
|
||||
# using a manually managed SVN checkout as opposed to a gclient managed checkout
|
||||
# you can still use the hooks mechanism for generating project files by calling
|
||||
# 'gclient runhooks' rather than 'gclient sync'.
|
||||
|
||||
deps = {
|
||||
# Logging code.
|
||||
"src/src/third_party/glog":
|
||||
"https://github.com/google/glog.git" +
|
||||
"@v0.3.4",
|
||||
|
||||
# Testing libraries and utilities.
|
||||
"src/src/testing":
|
||||
"https://github.com/google/googlemock.git" +
|
||||
"@release-1.7.0",
|
||||
"src/src/testing/gtest":
|
||||
"https://github.com/google/googletest.git" +
|
||||
"@release-1.7.0",
|
||||
|
||||
# Protobuf.
|
||||
"src/src/third_party/protobuf/protobuf":
|
||||
"https://github.com/google/protobuf.git" +
|
||||
"@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac",
|
||||
|
||||
# GYP project generator.
|
||||
"src/src/tools/gyp":
|
||||
"https://chromium.googlesource.com/external/gyp/" +
|
||||
"@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
|
||||
|
||||
# Linux syscall support.
|
||||
"src/src/third_party/lss":
|
||||
"https://chromium.googlesource.com/linux-syscall-support/" +
|
||||
"@9292030109847793f7a6689adac1ddafb412fe14"
|
||||
}
|
||||
|
||||
# We only use this file to ease the steps of generating projects after
|
||||
# syncing, if we use gclient. All dependencies are svn:externals instead.
|
||||
# If you're not using gclient, you need to run the gyp python script to
|
||||
# generate the projects.
|
||||
# This can be done by the following command (assuming current directory):
|
||||
# src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp
|
||||
hooks = [
|
||||
{
|
||||
# TODO(chrisha): Fix the GYP files so that they work without
|
||||
# --no-circular-check.
|
||||
# A change to a .gyp, .gypi, or to GYP itself should run the generator.
|
||||
"pattern": ".",
|
||||
"action": ["python",
|
||||
"src/src/tools/gyp/gyp_main.py",
|
||||
"--no-circular-check",
|
||||
"src/src/tools/gyp/gyp",
|
||||
"src/src/client/windows/breakpad_client.gyp"],
|
||||
},
|
||||
]
|
||||
|
210
INSTALL
210
INSTALL
@ -1,25 +1,19 @@
|
||||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
|
||||
Inc.
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||
2006 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. This file is offered as-is,
|
||||
without warranty of any kind.
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package. Some packages provide this
|
||||
`INSTALL' file but do not implement all of the features documented
|
||||
below. The lack of an optional feature in a given package is not
|
||||
necessarily a bug. More recommendations for GNU packages can be found
|
||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||
instructions specific to this package.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
@ -48,7 +42,7 @@ may remove or edit it.
|
||||
you want to change it or regenerate `configure' using a newer version
|
||||
of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system.
|
||||
@ -59,22 +53,12 @@ of `autoconf'.
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package, generally using the just-built uninstalled binaries.
|
||||
the package.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation. When installing into a prefix owned by root, it is
|
||||
recommended that the package be configured and built as a regular
|
||||
user, and only the `make install' phase executed with root
|
||||
privileges.
|
||||
documentation.
|
||||
|
||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||
this time using the binaries in their final installed location.
|
||||
This target does not install anything. Running this target as a
|
||||
regular user, particularly if the prior `make install' required
|
||||
root privileges, verifies that the installation completed
|
||||
correctly.
|
||||
|
||||
6. You can remove the program binaries and object files from the
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
@ -83,22 +67,12 @@ of `autoconf'.
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
7. Often, you can also type `make uninstall' to remove the installed
|
||||
files again. In practice, not all packages have tested that
|
||||
uninstallation works correctly, even though it is required by the
|
||||
GNU Coding Standards.
|
||||
|
||||
8. Some packages, particularly those that use Automake, provide `make
|
||||
distcheck', which can by used by developers to test that all other
|
||||
targets like `make install' and `make uninstall' work correctly.
|
||||
This target is generally not run by end users.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. Run `./configure --help'
|
||||
for details on some of the pertinent environment variables.
|
||||
Some systems require unusual options for compilation or linking that the
|
||||
`configure' script does not know about. Run `./configure --help' for
|
||||
details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
@ -111,41 +85,25 @@ is an example:
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'. This
|
||||
is known as a "VPATH" build.
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
With a non-GNU `make', it is safer to compile the package for one
|
||||
architecture at a time in the source code directory. After you have
|
||||
installed the package for one architecture, use `make distclean' before
|
||||
reconfiguring for another architecture.
|
||||
|
||||
On MacOS X 10.5 and later systems, you can create libraries and
|
||||
executables that work on multiple system types--known as "fat" or
|
||||
"universal" binaries--by specifying multiple `-arch' options to the
|
||||
compiler but only a single `-arch' option to the preprocessor. Like
|
||||
this:
|
||||
|
||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CPP="gcc -E" CXXCPP="g++ -E"
|
||||
|
||||
This is not guaranteed to produce working output in all cases, you
|
||||
may have to build one architecture at a time and combine the results
|
||||
using the `lipo' tool if you have problems.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||
absolute file name.
|
||||
`configure' the option `--prefix=PREFIX'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
@ -156,47 +114,16 @@ Documentation and other data files still use the regular prefix.
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them. In general, the
|
||||
default for these options is expressed in terms of `${prefix}', so that
|
||||
specifying just `--prefix' will affect all of the other directory
|
||||
specifications that were not explicitly provided.
|
||||
|
||||
The most portable way to affect installation locations is to pass the
|
||||
correct locations to `configure'; however, many packages provide one or
|
||||
both of the following shortcuts of passing variable assignments to the
|
||||
`make install' command line to change installation locations without
|
||||
having to reconfigure or recompile.
|
||||
|
||||
The first method involves providing an override variable for each
|
||||
affected directory. For example, `make install
|
||||
prefix=/alternate/directory' will choose an alternate location for all
|
||||
directory configuration variables that were expressed in terms of
|
||||
`${prefix}'. Any directories that were specified during `configure',
|
||||
but not in terms of `${prefix}', must each be overridden at install
|
||||
time for the entire installation to be relocated. The approach of
|
||||
makefile variable overrides for each directory variable is required by
|
||||
the GNU Coding Standards, and ideally causes no recompilation.
|
||||
However, some platforms have known limitations with the semantics of
|
||||
shared libraries that end up requiring recompilation when using this
|
||||
method, particularly noticeable in packages that use GNU Libtool.
|
||||
|
||||
The second method involves providing the `DESTDIR' variable. For
|
||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||
`/alternate/directory' before all installation names. The approach of
|
||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||
does not work on platforms that have drive letters. On the other hand,
|
||||
it does better at avoiding recompilation issues, and works well even
|
||||
when some directory options were not specified in terms of `${prefix}'
|
||||
at `configure' time.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
you can set and what kinds of files go in them.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
@ -208,58 +135,14 @@ find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Some packages offer the ability to configure how verbose the
|
||||
execution of `make' will be. For these packages, running `./configure
|
||||
--enable-silent-rules' sets the default to minimal output, which can be
|
||||
overridden with `make V=1'; while running `./configure
|
||||
--disable-silent-rules' sets the default to verbose, which can be
|
||||
overridden with `make V=0'.
|
||||
|
||||
Particular systems
|
||||
==================
|
||||
|
||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||
CC is not installed, it is recommended to use the following options in
|
||||
order to use an ANSI C compiler:
|
||||
|
||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||
|
||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||
|
||||
HP-UX `make' updates targets which have the same time stamps as
|
||||
their prerequisites, which makes it generally unusable when shipped
|
||||
generated files such as `configure' are involved. Use GNU `make'
|
||||
instead.
|
||||
|
||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||
to try
|
||||
|
||||
./configure CC="cc"
|
||||
|
||||
and if that doesn't work, try
|
||||
|
||||
./configure CC="cc -nodtk"
|
||||
|
||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||
directory contains several dysfunctional programs; working variants of
|
||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||
in your `PATH', put it _after_ `/usr/bin'.
|
||||
|
||||
On Haiku, software installed for all users goes in `/boot/common',
|
||||
not `/usr/local'. It is recommended to use the following options:
|
||||
|
||||
./configure --prefix=/boot/common
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out
|
||||
automatically, but needs to determine by the type of machine the package
|
||||
will run on. Usually, assuming the package is built to be run on the
|
||||
_same_ architectures, `configure' can figure that out, but if it prints
|
||||
a message saying it cannot guess the machine type, give it the
|
||||
There may be some features `configure' cannot figure out automatically,
|
||||
but needs to determine by the type of machine the package will run on.
|
||||
Usually, assuming the package is built to be run on the _same_
|
||||
architectures, `configure' can figure that out, but if it prints a
|
||||
message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
@ -267,8 +150,7 @@ type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS
|
||||
KERNEL-OS
|
||||
OS KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
@ -286,9 +168,9 @@ eventually be run) with `--host=TYPE'.
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
If you want to set default values for `configure' scripts to share, you
|
||||
can create a site shell script called `config.site' that gives default
|
||||
values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
@ -297,7 +179,7 @@ A warning: not all `configure' scripts look for a site script.
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
@ -309,27 +191,18 @@ causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||
an Autoconf limitation. Until the limitation is lifted, you can use
|
||||
this workaround:
|
||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
|
||||
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
`configure' recognizes the following options to control how it operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of all of the options to `configure', and exit.
|
||||
|
||||
`--help=short'
|
||||
`--help=recursive'
|
||||
Print a summary of the options unique to this package's
|
||||
`configure', and exit. The `short' variant lists options used
|
||||
only in the top level, while the `recursive' variant lists options
|
||||
also present in any nested packages.
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
@ -356,15 +229,6 @@ operates.
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--prefix=DIR'
|
||||
Use DIR as the installation prefix. *note Installation Names::
|
||||
for more details, including other options available for fine-tuning
|
||||
the installation locations.
|
||||
|
||||
`--no-create'
|
||||
`-n'
|
||||
Run the configure checks, but stop before creating any output
|
||||
files.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
||||
|
1144
Makefile.am
1144
Makefile.am
File diff suppressed because it is too large
Load Diff
7802
Makefile.in
7802
Makefile.in
File diff suppressed because it is too large
Load Diff
43
README
Normal file
43
README
Normal file
@ -0,0 +1,43 @@
|
||||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
|
||||
-----
|
||||
Getting started in 32-bit mode (from trunk)
|
||||
Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
|
||||
Build: make
|
||||
Test: make check
|
||||
Install: make install
|
||||
|
||||
If you need to reconfigure your build be sure to run "make distclean" first.
|
||||
|
||||
|
||||
-----
|
||||
To request change review:
|
||||
0. Get access to a read-write copy of source.
|
||||
Owners at http://code.google.com/p/google-breakpad/ are able to grant
|
||||
this access.
|
||||
|
||||
1. Check out a read-write copy of source using instructions at
|
||||
http://code.google.com/p/google-breakpad/source/checkout
|
||||
|
||||
2. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
3. Download http://codereview.appspot.com/static/upload.py
|
||||
|
||||
4. Run upload.py from the 'src' directory:
|
||||
upload.py --server=breakpad.appspot.com
|
||||
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
5. At http://breakpad.appspot.com you'll find your issue listed; click on it,
|
||||
and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
||||
|
||||
6. When applying code review feedback, specify the '-i' option when running
|
||||
upload.py again and pass the issue number so it updates the existing issue,
|
||||
rather than creating a new one.
|
||||
Be sure to rerun upload.py from the same directory as you did for previous
|
||||
uploads to allow for proper diff calculations.
|
139
README.ANDROID
139
README.ANDROID
@ -1,139 +0,0 @@
|
||||
Google Breakpad for Android
|
||||
===========================
|
||||
|
||||
This document explains how to use the Google Breakpad client library
|
||||
on Android, and later generate valid stack traces from the minidumps
|
||||
it generates.
|
||||
|
||||
This release supports ARM, x86 and MIPS based Android systems.
|
||||
This release requires NDK release r11c or higher.
|
||||
|
||||
I. Building the client library:
|
||||
===============================
|
||||
|
||||
The Android client is built as a static library that you can
|
||||
link into your own Android native code. There are two ways to
|
||||
build it:
|
||||
|
||||
I.1. Building with ndk-build:
|
||||
-----------------------------
|
||||
|
||||
If you're using the ndk-build build system, you can follow
|
||||
these simple steps:
|
||||
|
||||
1/ Include android/google_breakpad/Android.mk from your own
|
||||
project's Android.mk
|
||||
|
||||
This can be done either directly, or using ndk-build's
|
||||
import-module feature.
|
||||
|
||||
2/ Link the library to one of your modules by using:
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += breakpad_client
|
||||
|
||||
NOTE: The client library requires a C++ STL implementation,
|
||||
which you can select with APP_STL in your Application.mk
|
||||
|
||||
It has been tested succesfully with both STLport and GNU libstdc++
|
||||
|
||||
|
||||
I.2. Building with a standalone Android toolchain:
|
||||
--------------------------------------------------
|
||||
|
||||
All you need to do is configure your build with the right 'host'
|
||||
value, and disable the processor and tools, as in:
|
||||
|
||||
$GOOGLE_BREAKPAD_PATH/configure --host=arm-linux-androideabi \
|
||||
--disable-processor \
|
||||
--disable-tools
|
||||
make -j4
|
||||
|
||||
The library will be under src/client/linux/libbreakpad_client.a
|
||||
|
||||
You can also use 'make check' to run the test suite on a connected
|
||||
Android device. This requires the Android 'adb' tool to be in your
|
||||
path.
|
||||
|
||||
II. Using the client library in Android:
|
||||
========================================
|
||||
|
||||
The usage instructions are very similar to the Linux ones that are
|
||||
found at https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/linux_starter_guide.md
|
||||
|
||||
1/ You need to include "client/linux/handler/exception_handler.h" from a C++
|
||||
source file.
|
||||
|
||||
2/ If you're not using ndk-build, you also need to:
|
||||
|
||||
- add the following to your compiler include search paths:
|
||||
$GOOGLE_BREAKPAD_PATH/src
|
||||
$GOOGLE_BREAKPAD_PATH/src/common/android/include
|
||||
|
||||
- add -llog to your linker flags
|
||||
|
||||
Note that ndk-build does that for your automatically.
|
||||
|
||||
3/ Keep in mind that there is no /tmp directory on Android.
|
||||
|
||||
If you use the library from a regular Android applications, specify a
|
||||
path under your app-specific storage directory. An alternative is to
|
||||
store them on the SDCard, but this requires a specific permission.
|
||||
|
||||
For a concrete example, see the sample test application under
|
||||
android/sample_app. See its README for more information.
|
||||
|
||||
|
||||
III. Getting a stack trace on the host:
|
||||
=======================================
|
||||
|
||||
This process is similar to other platforms, but here's a quick example:
|
||||
|
||||
1/ Retrieve the minidumps on your development machine.
|
||||
|
||||
2/ Dump the symbols for your native libraries with the 'dump_syms' tool.
|
||||
This first requires building the host version of Google Breakpad, then
|
||||
calling:
|
||||
|
||||
dump_syms $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym
|
||||
|
||||
3/ Create the symbol directory hierarchy.
|
||||
|
||||
The first line of the generated libfoo.so.sym will have a "MODULE"
|
||||
entry that carries a hexadecimal version number, e.g.:
|
||||
|
||||
MODULE Linux arm D51B4A5504974FA6ECC1869CAEE3603B0 test_google_breakpad
|
||||
|
||||
Note: The second field could be either 'Linux' or 'Android'.
|
||||
|
||||
Extract the version number, and a 'symbol' directory, for example:
|
||||
|
||||
$PROJECT_PATH/symbols/libfoo.so/$VERSION/
|
||||
|
||||
Copy/Move your libfoo.sym file there.
|
||||
|
||||
4/ Invoke minidump_stackwalk to create the stack trace:
|
||||
|
||||
minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols
|
||||
|
||||
Note that various helper scripts can be found on the web to automate these
|
||||
steps.
|
||||
|
||||
IV. Verifying the Android build library:
|
||||
========================================
|
||||
|
||||
If you modify Google Breakpad and want to check that it still works correctly
|
||||
on Android, please run the android/run-checks.sh script which will do all
|
||||
necessary verifications for you. This includes:
|
||||
|
||||
- Rebuilding the full host binaries.
|
||||
- Rebuilding the full Android binaries with configure/make.
|
||||
- Rebuilding the client library unit tests, and running them on a device.
|
||||
- Rebuilding the client library with ndk-build.
|
||||
- Building, installing and running a test crasher program on a device.
|
||||
- Extracting the corresponding minidump, dumping the test program symbols
|
||||
and generating a stack trace.
|
||||
- Checking the generated stack trace for valid source locations.
|
||||
|
||||
For more details, please run:
|
||||
|
||||
android/run-checks.sh --help-all
|
80
README.md
80
README.md
@ -1,80 +0,0 @@
|
||||
# Breakpad
|
||||
|
||||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
* [Homepage](https://chromium.googlesource.com/breakpad/breakpad/)
|
||||
* [Documentation](https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/)
|
||||
* [Bugs](https://bugs.chromium.org/p/google-breakpad/)
|
||||
* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss)
|
||||
* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev)
|
||||
* Tests: [](https://travis-ci.org/google/breakpad)
|
||||
|
||||
## Getting started (from master)
|
||||
|
||||
1. First, [download depot_tools](http://dev.chromium.org/developers/how-tos/install-depot-tools)
|
||||
and ensure that they're in your `PATH`.
|
||||
|
||||
2. Create a new directory for checking out the source code (it must be named
|
||||
breakpad).
|
||||
|
||||
```sh
|
||||
mkdir breakpad && cd breakpad
|
||||
```
|
||||
|
||||
3. Run the `fetch` tool from depot_tools to download all the source repos.
|
||||
|
||||
```sh
|
||||
fetch breakpad
|
||||
cd src
|
||||
```
|
||||
|
||||
4. Build the source.
|
||||
|
||||
```sh
|
||||
./configure && make
|
||||
```
|
||||
|
||||
You can also cd to another directory and run configure from there to build
|
||||
outside the source tree.
|
||||
|
||||
This will build the processor tools (`src/processor/minidump_stackwalk`,
|
||||
`src/processor/minidump_dump`, etc), and when building on Linux it will
|
||||
also build the client libraries and some tools
|
||||
(`src/tools/linux/dump_syms/dump_syms`,
|
||||
`src/tools/linux/md2core/minidump-2-core`, etc).
|
||||
|
||||
5. Optionally, run tests.
|
||||
|
||||
```sh
|
||||
make check
|
||||
```
|
||||
|
||||
6. Optionally, install the built libraries
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
|
||||
If you need to reconfigure your build be sure to run `make distclean` first.
|
||||
|
||||
To update an existing checkout to a newer revision, you can
|
||||
`git pull` as usual, but then you should run `gclient sync` to ensure that the
|
||||
dependent repos are up-to-date.
|
||||
|
||||
## To request change review
|
||||
|
||||
1. Follow the steps above to get the source and build it.
|
||||
|
||||
2. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
3. Commit your changes to your local repo and upload them to the server.
|
||||
http://dev.chromium.org/developers/contributing-code
|
||||
e.g. `git commit ... && git cl upload ...`
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
4. At https://codereview.chromium.org/ you'll find your issue listed; click on
|
||||
it, and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
876
aclocal.m4
vendored
876
aclocal.m4
vendored
File diff suppressed because it is too large
Load Diff
@ -1,372 +0,0 @@
|
||||
# Copyright (c) 2012 Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Collection of common shell functions for 'run-checks.sh' et 'test-shell.sh'
|
||||
|
||||
# All internal variables and functions use an underscore as a prefix
|
||||
# (e.g. _VERBOSE, _ALL_CLEANUPS, etc..).
|
||||
|
||||
# Sanitize the environment
|
||||
export LANG=C
|
||||
export LC_ALL=C
|
||||
|
||||
if [ "$BASH_VERSION" ]; then
|
||||
set -o posix
|
||||
fi
|
||||
|
||||
# Utility functions
|
||||
|
||||
_ALL_CLEANUPS=
|
||||
|
||||
# Register a function to be called when the script exits, even in case of
|
||||
# Ctrl-C, logout, etc.
|
||||
# $1: function name.
|
||||
atexit () {
|
||||
if [ -z "$_ALL_CLEANUPS" ]; then
|
||||
_ALL_CLEANUPS=$1
|
||||
# Ensure a clean exit when the script is:
|
||||
# - Exiting normally (EXIT)
|
||||
# - Interrupted by Ctrl-C (INT)
|
||||
# - Interrupted by log out (HUP)
|
||||
# - Being asked to quit nicely (TERM)
|
||||
# - Being asked to quit and dump core (QUIT)
|
||||
trap "_exit_cleanups \$?" EXIT INT HUP QUIT TERM
|
||||
else
|
||||
_ALL_CLEANUPS="$_ALL_CLEANUPS $1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Called on exit if at least one function was registered with atexit
|
||||
# $1: final exit status code
|
||||
_exit_cleanups () {
|
||||
local CLEANUP CLEANUPS
|
||||
# Ignore calls to atexit during cleanups
|
||||
CLEANUPS=$_ALL_CLEANUPS
|
||||
_ALL_CLEANUPS=
|
||||
for CLEANUP in $CLEANUPS; do
|
||||
($CLEANUP)
|
||||
done
|
||||
exit "$@"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# Dump a panic message then exit.
|
||||
# $1+: message
|
||||
panic () {
|
||||
echo "ERROR: $@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If the previous command failed, dump a panic message then exit.
|
||||
# $1+: message.
|
||||
fail_panic () {
|
||||
if [ $? != 0 ]; then
|
||||
panic "$@"
|
||||
fi;
|
||||
}
|
||||
|
||||
_VERBOSE=0
|
||||
|
||||
# Increase verbosity for dump/log/run/run2 functions
|
||||
increase_verbosity () {
|
||||
_VERBOSE=$(( $_VERBOSE + 1 ))
|
||||
}
|
||||
|
||||
# Decrease verbosity
|
||||
decrease_verbosity () {
|
||||
_VERBOSE=$(( $_VERBOSE - 1 ))
|
||||
}
|
||||
|
||||
# Returns success iff verbosity level is higher than a specific value
|
||||
# $1: verbosity level
|
||||
verbosity_is_higher_than () {
|
||||
[ "$_VERBOSE" -gt "$1" ]
|
||||
}
|
||||
|
||||
# Returns success iff verbosity level is lower than a specific value
|
||||
# $1: verbosity level
|
||||
verbosity_is_lower_than () {
|
||||
[ "$_VERBOSE" -le "$1" ]
|
||||
}
|
||||
|
||||
# Dump message to stdout, unless verbosity is < 0, i.e. --quiet was called
|
||||
# $1+: message
|
||||
dump () {
|
||||
if [ "$_VERBOSE" -ge 0 ]; then
|
||||
printf "%s\n" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
# If --verbose was used, dump a message to stdout.
|
||||
# $1+: message
|
||||
log () {
|
||||
if [ "$_VERBOSE" -ge 1 ]; then
|
||||
printf "%s\n" "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
_RUN_LOG=
|
||||
|
||||
# Set a run log file that can be used to collect the output of commands that
|
||||
# are not displayed.
|
||||
set_run_log () {
|
||||
_RUN_LOG=$1
|
||||
}
|
||||
|
||||
# Run a command. Output depends on $_VERBOSE:
|
||||
# $_VERBOSE <= 0: Run command, store output into the run log
|
||||
# $_VERBOSE >= 1: Dump command, run it, output goest to stdout
|
||||
# Note: Ideally, the command's output would go to the run log for $_VERBOSE >= 1
|
||||
# but the 'tee' tool doesn't preserve the status code of its input pipe
|
||||
# in case of error.
|
||||
run () {
|
||||
local LOGILE
|
||||
if [ "$_RUN_LOG" ]; then
|
||||
LOGFILE=$_RUN_LOG
|
||||
else
|
||||
LOGFILE=/dev/null
|
||||
fi
|
||||
|
||||
if [ "$_VERBOSE" -ge 1 ]; then
|
||||
echo "COMMAND: $@"
|
||||
"$@"
|
||||
else
|
||||
"$@" >>$LOGFILE 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Same as run(), but only dump command output for $_VERBOSE >= 2
|
||||
run2 () {
|
||||
local LOGILE
|
||||
if [ "$_RUN_LOG" ]; then
|
||||
LOGFILE=$_RUN_LOG
|
||||
else
|
||||
LOGFILE=/dev/null
|
||||
fi
|
||||
|
||||
if [ "$_VERBOSE" -ge 1 ]; then
|
||||
echo "COMMAND: $@"
|
||||
fi
|
||||
if [ "$_VERBOSE" -ge 2 ]; then
|
||||
"$@"
|
||||
else
|
||||
"$@" >>$LOGFILE 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Extract number of cores to speed up the builds
|
||||
# Out: number of CPU cores
|
||||
get_core_count () {
|
||||
case $(uname -s) in
|
||||
Linux)
|
||||
grep -c -e '^processor' /proc/cpuinfo
|
||||
;;
|
||||
Darwin)
|
||||
sysctl -n hw.ncpu
|
||||
;;
|
||||
CYGWIN*|*_NT-*)
|
||||
echo $NUMBER_OF_PROCESSORS
|
||||
;;
|
||||
*)
|
||||
echo 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
# Check for the Android ADB program.
|
||||
#
|
||||
# On success, return nothing, but updates internal variables so later calls to
|
||||
# adb_shell, adb_push, etc.. will work. You can get the path to the ADB program
|
||||
# with adb_get_program if needed.
|
||||
#
|
||||
# On failure, returns 1, and updates the internal adb error message, which can
|
||||
# be retrieved with adb_get_error.
|
||||
#
|
||||
# $1: optional ADB program path.
|
||||
# Return: success or failure.
|
||||
_ADB=
|
||||
_ADB_STATUS=
|
||||
_ADB_ERROR=
|
||||
|
||||
adb_check () {
|
||||
# First, try to find the executable in the path, or the SDK install dir.
|
||||
_ADB=$1
|
||||
if [ -z "$_ADB" ]; then
|
||||
_ADB=$(which adb 2>/dev/null)
|
||||
if [ -z "$_ADB" -a "$ANDROID_SDK_ROOT" ]; then
|
||||
_ADB=$ANDROID_SDK_ROOT/platform-tools/adb
|
||||
if [ ! -f "$_ADB" ]; then
|
||||
_ADB=
|
||||
fi
|
||||
fi
|
||||
if [ -z "$_ADB" ]; then
|
||||
_ADB_STATUS=1
|
||||
_ADB_ERROR="The Android 'adb' tool is not in your path."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Found ADB program: $_ADB"
|
||||
|
||||
# Check that it works correctly
|
||||
local ADB_VERSION
|
||||
ADB_VERSION=$("$_ADB" version 2>/dev/null)
|
||||
case $ADB_VERSION in
|
||||
"Android Debug Bridge "*) # Pass
|
||||
log "Found ADB version: $ADB_VERSION"
|
||||
;;
|
||||
*) # Fail
|
||||
_ADB_ERROR="Your ADB binary reports a bad version ($ADB_VERSION): $_ADB"
|
||||
_ADB_STATUS=1
|
||||
return 1
|
||||
esac
|
||||
|
||||
_ADB_STATUS=0
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Return the path to the Android ADB program, if correctly detected.
|
||||
# On failure, return the empty string.
|
||||
# Out: ADB program path (or empty on failure)
|
||||
# Return: success or failure.
|
||||
adb_get_program () {
|
||||
# Return cached value as soon as possible.
|
||||
if [ -z "$_ADB_STATUS" ]; then
|
||||
adb_check $1
|
||||
fi
|
||||
echo "$_ADB"
|
||||
return $_ADB_STATUS
|
||||
}
|
||||
|
||||
# Return the error corresponding to the last ADB function failure.
|
||||
adb_get_error () {
|
||||
echo "$_ADB_ERROR"
|
||||
}
|
||||
|
||||
# Check that there is one device connected through ADB.
|
||||
# In case of failure, use adb_get_error to know why this failed.
|
||||
# $1: Optional adb program path
|
||||
# Return: success or failure.
|
||||
_ADB_DEVICE=
|
||||
_ADB_DEVICE_STATUS=
|
||||
adb_check_device () {
|
||||
if [ "$_ADB_DEVICE_STATUS" ]; then
|
||||
return $_ADB_DEVICE_STATUS
|
||||
fi
|
||||
|
||||
# Check for ADB.
|
||||
if ! adb_check $1; then
|
||||
_ADB_DEVICE_STATUS=$_ADB_STATUS
|
||||
return 1
|
||||
fi
|
||||
|
||||
local ADB_DEVICES NUM_DEVICES FINGERPRINT
|
||||
|
||||
# Count the number of connected devices.
|
||||
ADB_DEVICES=$("$_ADB" devices 2>/dev/null | awk '$2 == "device" { print $1; }')
|
||||
NUM_DEVICES=$(echo "$ADB_DEVICES" | wc -l)
|
||||
case $NUM_DEVICES in
|
||||
0)
|
||||
_ADB_ERROR="No Android device connected. Please connect one to your machine."
|
||||
_ADB_DEVICE_STATUS=1
|
||||
return 1
|
||||
;;
|
||||
1) # Pass
|
||||
# Ensure the same device will be called in later adb_shell calls.
|
||||
export ANDROID_SERIAL=$ADB_DEVICES
|
||||
;;
|
||||
*) # 2 or more devices.
|
||||
if [ "$ANDROID_SERIAL" ]; then
|
||||
ADB_DEVICES=$ANDROID_SERIAL
|
||||
NUM_DEVICES=1
|
||||
else
|
||||
_ADB_ERROR="More than one Android device connected. \
|
||||
Please define ANDROID_SERIAL in your environment"
|
||||
_ADB_DEVICE_STATUS=1
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
_ADB_DEVICE_STATUS=0
|
||||
_ADB_DEVICE=$ADB_DEVICES
|
||||
|
||||
FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
|
||||
log "Using ADB device: $ANDROID_SERIAL ($FINGERPRINT)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# The 'adb shell' command is pretty hopeless, try to make sense of it by:
|
||||
# 1/ Removing trailing \r from line endings.
|
||||
# 2/ Ensuring the function returns the command's status code.
|
||||
#
|
||||
# $1+: Command
|
||||
# Out: command output (stdout + stderr combined)
|
||||
# Return: command exit status
|
||||
adb_shell () {
|
||||
local RET ADB_LOG
|
||||
# Check for ADB device.
|
||||
adb_check_device || return 1
|
||||
ADB_LOG=$(mktemp "${TMPDIR:-/tmp}/adb-XXXXXXXX")
|
||||
"$_ADB" shell "$@" ";" echo \$? > "$ADB_LOG" 2>&1
|
||||
sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG" # Remove \r.
|
||||
RET=$(sed -e '$!d' "$ADB_LOG") # Last line contains status code.
|
||||
sed -e '$d' "$ADB_LOG" # Print everything except last line.
|
||||
rm -f "$ADB_LOG"
|
||||
return $RET
|
||||
}
|
||||
|
||||
# Push a file to a device.
|
||||
# $1: source file path
|
||||
# $2: device target file path
|
||||
# Return: success or failure.
|
||||
adb_push () {
|
||||
adb_check_device || return 1
|
||||
run "$_ADB" push "$1" "$2"
|
||||
}
|
||||
|
||||
# Pull a file from a device
|
||||
# $1: device file path
|
||||
# $2: target host file path
|
||||
# Return: success or failure.
|
||||
adb_pull () {
|
||||
adb_check_device || return 1
|
||||
run "$_ADB" pull "$1" "$2"
|
||||
}
|
||||
|
||||
# Same as adb_push, but will panic if the operations didn't succeed.
|
||||
adb_install () {
|
||||
adb_push "$@"
|
||||
fail_panic "Failed to install $1 to the Android device at $2"
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
# Copyright (c) 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# ndk-build module definition for the Google Breakpad client library
|
||||
#
|
||||
# To use this file, do the following:
|
||||
#
|
||||
# 1/ Include this file from your own Android.mk, either directly
|
||||
# or with through the NDK's import-module function.
|
||||
#
|
||||
# 2/ Use the client static library in your project with:
|
||||
#
|
||||
# LOCAL_STATIC_LIBRARIES += breakpad_client
|
||||
#
|
||||
# 3/ In your source code, include "src/client/linux/exception_handler.h"
|
||||
# and use the Linux instructions to use it.
|
||||
#
|
||||
# This module works with either the STLport or GNU libstdc++, but you need
|
||||
# to select one in your Application.mk
|
||||
#
|
||||
|
||||
# The top Google Breakpad directory.
|
||||
# We assume this Android.mk to be under 'android/google_breakpad'
|
||||
|
||||
LOCAL_PATH := $(call my-dir)/../..
|
||||
|
||||
# Defube the client library module, as a simple static library that
|
||||
# exports the right include path / linker flags to its users.
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := breakpad_client
|
||||
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
|
||||
# Breakpad uses inline ARM assembly that requires the library
|
||||
# to be built in ARM mode. Otherwise, the build will fail with
|
||||
# cryptic assembler messages like:
|
||||
# Compile++ thumb : google_breakpad_client <= crash_generation_client.cc
|
||||
# /tmp/cc8aMSoD.s: Assembler messages:
|
||||
# /tmp/cc8aMSoD.s:132: Error: invalid immediate: 288 is out of range
|
||||
# /tmp/cc8aMSoD.s:244: Error: invalid immediate: 296 is out of range
|
||||
LOCAL_ARM_MODE := arm
|
||||
|
||||
# List of client source files, directly taken from Makefile.am
|
||||
LOCAL_SRC_FILES := \
|
||||
src/client/linux/crash_generation/crash_generation_client.cc \
|
||||
src/client/linux/dump_writer_common/thread_info.cc \
|
||||
src/client/linux/dump_writer_common/ucontext_reader.cc \
|
||||
src/client/linux/handler/exception_handler.cc \
|
||||
src/client/linux/handler/minidump_descriptor.cc \
|
||||
src/client/linux/log/log.cc \
|
||||
src/client/linux/microdump_writer/microdump_writer.cc \
|
||||
src/client/linux/minidump_writer/linux_dumper.cc \
|
||||
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
|
||||
src/client/linux/minidump_writer/minidump_writer.cc \
|
||||
src/client/minidump_file_writer.cc \
|
||||
src/common/android/breakpad_getcontext.S \
|
||||
src/common/convert_UTF.c \
|
||||
src/common/md5.cc \
|
||||
src/common/string_conversion.cc \
|
||||
src/common/linux/elfutils.cc \
|
||||
src/common/linux/file_id.cc \
|
||||
src/common/linux/guid_creator.cc \
|
||||
src/common/linux/linux_libc_support.cc \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
src/common/linux/safe_readlink.cc
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \
|
||||
$(LOCAL_PATH)/src
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_EXPORT_LDLIBS := -llog
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# Done.
|
@ -1,555 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) 2012 Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Sanitize the environment
|
||||
export LANG=C
|
||||
export LC_ALL=C
|
||||
|
||||
if [ "$BASH_VERSION" ]; then
|
||||
set -o posix
|
||||
fi
|
||||
|
||||
PROGDIR=$(dirname "$0")
|
||||
PROGDIR=$(cd "$PROGDIR" && pwd)
|
||||
PROGNAME=$(basename "$0")
|
||||
|
||||
. $PROGDIR/common-functions.sh
|
||||
|
||||
DEFAULT_ABI="armeabi"
|
||||
VALID_ABIS="armeabi armeabi-v7a x86 mips"
|
||||
|
||||
ABI=
|
||||
ADB=
|
||||
ALL_TESTS=
|
||||
ENABLE_M32=
|
||||
HELP=
|
||||
HELP_ALL=
|
||||
NDK_DIR=
|
||||
NO_CLEANUP=
|
||||
NO_DEVICE=
|
||||
NUM_JOBS=$(get_core_count)
|
||||
TMPDIR=
|
||||
|
||||
for opt do
|
||||
# The following extracts the value if the option is like --name=<value>.
|
||||
optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$')
|
||||
case $opt in
|
||||
--abi=*) ABI=$optarg;;
|
||||
--adb=*) ADB=$optarg;;
|
||||
--all-tests) ALL_TESTS=true;;
|
||||
--enable-m32) ENABLE_M32=true;;
|
||||
--help|-h|-?) HELP=TRUE;;
|
||||
--help-all) HELP_ALL=true;;
|
||||
--jobs=*) NUM_JOBS=$optarg;;
|
||||
--ndk-dir=*) NDK_DIR=$optarg;;
|
||||
--tmp-dir=*) TMPDIR=$optarg;;
|
||||
--no-cleanup) NO_CLEANUP=true;;
|
||||
--no-device) NO_DEVICE=true;;
|
||||
--quiet) decrease_verbosity;;
|
||||
--verbose) increase_verbosity;;
|
||||
-*) panic "Invalid option '$opt', see --help for details.";;
|
||||
*) panic "This script doesn't take any parameters. See --help for details."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$HELP" -o "$HELP_ALL" ]; then
|
||||
echo "\
|
||||
Usage: $PROGNAME [options]
|
||||
|
||||
This script is used to check that your Google Breakpad source tree can
|
||||
be properly built for Android, and that the client library and host tools
|
||||
work properly together.
|
||||
"
|
||||
if [ "$HELP_ALL" ]; then
|
||||
echo "\
|
||||
In more details, this script will:
|
||||
|
||||
- Rebuild the host version of Google Breakpad in a temporary
|
||||
directory (with the Auto-tools based build system).
|
||||
|
||||
- Rebuild the Android client library with the Google Breakpad build
|
||||
system (using autotools/configure). This requires that you define
|
||||
ANDROID_NDK_ROOT in your environment to point to a valid Android NDK
|
||||
installation directory, or use the --ndk-dir=<path> option.
|
||||
|
||||
- Rebuild the Android client library and a test crashing program with the
|
||||
Android NDK build system (ndk-build).
|
||||
|
||||
- Require an Android device connected to your machine, and the 'adb'
|
||||
tool in your path. They are used to:
|
||||
|
||||
- Install and run a test crashing program.
|
||||
- Extract the corresponding minidump from the device.
|
||||
- Dump the symbols from the test program on the host with 'dump_syms'
|
||||
- Generate a stack trace with 'minidump_stackwalk'
|
||||
- Check the stack trace content for valid source file locations.
|
||||
|
||||
You can however skip this requirement and only test the builds by using
|
||||
the --no-device flag.
|
||||
|
||||
By default, all generated files will be created in a temporary directory
|
||||
that is removed when the script completion. If you want to inspect the
|
||||
files, use the --no-cleanup option.
|
||||
|
||||
Finally, use --verbose to increase the verbosity level, this will help
|
||||
you see which exact commands are being issues and their result. Use the
|
||||
flag twice for even more output. Use --quiet to decrease verbosity
|
||||
instead and run the script silently.
|
||||
|
||||
If you have a device connected, the script will probe it to determine
|
||||
its primary CPU ABI, and build the test program for it. You can however
|
||||
use the --abi=<name> option to override this (this can be useful to check
|
||||
the secondary ABI, e.g. using --abi=armeabi to check that such a program
|
||||
works correctly on an ARMv7-A device).
|
||||
|
||||
If you don't have a device connected, the test program will be built (but
|
||||
not run) with the default '$DEFAULT_ABI' ABI. Again, you can use
|
||||
--abi=<name> to override this. Valid ABI names are:
|
||||
|
||||
$VALID_ABIS
|
||||
|
||||
The script will only run the client library unit test on the device
|
||||
by default. You can use --all-tests to also build and run the unit
|
||||
tests for the Breakpad tools and processor, but be warned that this
|
||||
adds several minutes of testing time. --all-tests will also run the
|
||||
host unit tests suite.
|
||||
"
|
||||
|
||||
fi # HELP_ALL
|
||||
|
||||
echo "\
|
||||
Valid options:
|
||||
|
||||
--help|-h|-? Display this message.
|
||||
--help-all Display extended help.
|
||||
--enable-m32 Build 32-bit version of host tools.
|
||||
--abi=<name> Specify target CPU ABI [auto-detected].
|
||||
--jobs=<count> Run <count> build tasks in parallel [$NUM_JOBS].
|
||||
--ndk-dir=<path> Specify NDK installation directory.
|
||||
--tmp-dir=<path> Specify temporary directory (will be wiped-out).
|
||||
--adb=<path> Specify adb program path.
|
||||
--no-cleanup Don't remove temporary directory after completion.
|
||||
--no-device Do not try to detect devices, nor run crash test.
|
||||
--all-tests Run all unit tests (i.e. tools and processor ones too).
|
||||
--verbose Increase verbosity.
|
||||
--quiet Decrease verbosity."
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TESTAPP_DIR=$PROGDIR/sample_app
|
||||
|
||||
# Select NDK install directory.
|
||||
if [ -z "$NDK_DIR" ]; then
|
||||
if [ -z "$ANDROID_NDK_ROOT" ]; then
|
||||
panic "Please define ANDROID_NDK_ROOT in your environment, or use \
|
||||
--ndk-dir=<path>."
|
||||
fi
|
||||
NDK_DIR="$ANDROID_NDK_ROOT"
|
||||
log "Found NDK directory: $NDK_DIR"
|
||||
else
|
||||
log "Using NDK directory: $NDK_DIR"
|
||||
fi
|
||||
# Small sanity check.
|
||||
NDK_BUILD="$NDK_DIR/ndk-build"
|
||||
if [ ! -f "$NDK_BUILD" ]; then
|
||||
panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR"
|
||||
fi
|
||||
|
||||
# Ensure the temporary directory is deleted on exit, except if the --no-cleanup
|
||||
# option is used.
|
||||
|
||||
clean_tmpdir () {
|
||||
if [ "$TMPDIR" ]; then
|
||||
if [ -z "$NO_CLEANUP" ]; then
|
||||
log "Cleaning up: $TMPDIR"
|
||||
rm -rf "$TMPDIR"
|
||||
else
|
||||
dump "Temporary directory contents preserved: $TMPDIR"
|
||||
fi
|
||||
fi
|
||||
exit "$@"
|
||||
}
|
||||
|
||||
atexit clean_tmpdir
|
||||
|
||||
# If --tmp-dir=<path> is not used, create a temporary directory.
|
||||
# Otherwise, start by cleaning up the user-provided path.
|
||||
if [ -z "$TMPDIR" ]; then
|
||||
TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX)
|
||||
fail_panic "Can't create temporary directory!"
|
||||
log "Using temporary directory: $TMPDIR"
|
||||
else
|
||||
if [ ! -d "$TMPDIR" ]; then
|
||||
mkdir -p "$TMPDIR"
|
||||
fail_panic "Can't create temporary directory: $TMPDIR"
|
||||
else
|
||||
log "Cleaning up temporary directory: $TMPDIR"
|
||||
rm -rf "$TMPDIR"/*
|
||||
fail_panic "Cannot cleanup temporary directory!"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$NO_DEVICE" ]; then
|
||||
if ! adb_check_device $ADB; then
|
||||
echo "$(adb_get_error)"
|
||||
echo "Use --no-device to build the code without running any tests."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
BUILD_LOG="$TMPDIR/build.log"
|
||||
RUN_LOG="$TMPDIR/run.log"
|
||||
CRASH_LOG="$TMPDIR/crash.log"
|
||||
|
||||
set_run_log "$RUN_LOG"
|
||||
|
||||
TMPHOST="$TMPDIR/host-local"
|
||||
|
||||
cd "$TMPDIR"
|
||||
|
||||
# Build host version of the tools
|
||||
dump "Building host binaries."
|
||||
CONFIGURE_FLAGS=
|
||||
if [ "$ENABLE_M32" ]; then
|
||||
CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32"
|
||||
fi
|
||||
(
|
||||
run mkdir "$TMPDIR/build-host" &&
|
||||
run cd "$TMPDIR/build-host" &&
|
||||
run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS &&
|
||||
run2 make -j$NUM_JOBS install
|
||||
)
|
||||
fail_panic "Can't build host binaries!"
|
||||
|
||||
if [ "$ALL_TESTS" ]; then
|
||||
dump "Running host unit tests."
|
||||
(
|
||||
run cd "$TMPDIR/build-host" &&
|
||||
run2 make -j$NUM_JOBS check
|
||||
)
|
||||
fail_panic "Host unit tests failed!!"
|
||||
fi
|
||||
|
||||
TMPBIN=$TMPHOST/bin
|
||||
|
||||
# Generate a stand-alone NDK toolchain
|
||||
|
||||
# Extract CPU ABI and architecture from device, if any.
|
||||
if adb_check_device; then
|
||||
DEVICE_ABI=$(adb_shell getprop ro.product.cpu.abi)
|
||||
DEVICE_ABI2=$(adb_shell getprop ro.product.cpu.abi2)
|
||||
if [ -z "$DEVICE_ABI" ]; then
|
||||
panic "Can't extract ABI from connected device!"
|
||||
fi
|
||||
if [ "$DEVICE_ABI2" ]; then
|
||||
dump "Found device ABIs: $DEVICE_ABI $DEVICE_ABI2"
|
||||
else
|
||||
dump "Found device ABI: $DEVICE_ABI"
|
||||
DEVICE_ABI2=$DEVICE_ABI
|
||||
fi
|
||||
|
||||
# If --abi=<name> is used, check that the device supports it.
|
||||
if [ "$ABI" -a "$DEVICE_ABI" != "$ABI" -a "$DEVICE_ABI2" != "$ABI" ]; then
|
||||
dump "ERROR: Device ABI(s) do not match --abi command-line value ($ABI)!"
|
||||
panic "Please use --no-device to skip device tests."
|
||||
fi
|
||||
|
||||
if [ -z "$ABI" ]; then
|
||||
ABI=$DEVICE_ABI
|
||||
dump "Using CPU ABI: $ABI (device)"
|
||||
else
|
||||
dump "Using CPU ABI: $ABI (command-line)"
|
||||
fi
|
||||
else
|
||||
if [ -z "$ABI" ]; then
|
||||
# No device connected, choose default ABI
|
||||
ABI=$DEFAULT_ABI
|
||||
dump "Using CPU ABI: $ABI (default)"
|
||||
else
|
||||
dump "Using CPU ABI: $ABI (command-line)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check the ABI value
|
||||
VALID=
|
||||
for VALID_ABI in $VALID_ABIS; do
|
||||
if [ "$ABI" = "$VALID_ABI" ]; then
|
||||
VALID=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$VALID" ]; then
|
||||
panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS"
|
||||
fi
|
||||
|
||||
# Extract architecture name from ABI
|
||||
case $ABI in
|
||||
armeabi*) ARCH=arm;;
|
||||
*) ARCH=$ABI;;
|
||||
esac
|
||||
|
||||
# Extract GNU configuration name
|
||||
case $ARCH in
|
||||
arm)
|
||||
GNU_CONFIG=arm-linux-androideabi
|
||||
;;
|
||||
x86)
|
||||
GNU_CONFIG=i686-linux-android
|
||||
;;
|
||||
mips)
|
||||
GNU_CONFIG=mipsel-linux-android
|
||||
;;
|
||||
*)
|
||||
GNU_CONFIG="$ARCH-linux-android"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Generate standalone NDK toolchain installation
|
||||
NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain"
|
||||
echo "Generating NDK standalone toolchain installation"
|
||||
mkdir -p "$NDK_STANDALONE"
|
||||
# NOTE: The --platform=android-9 is required to provide <regex.h> for GTest.
|
||||
run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \
|
||||
--arch="$ARCH" \
|
||||
--platform=android-9 \
|
||||
--install-dir="$NDK_STANDALONE"
|
||||
fail_panic "Can't generate standalone NDK toolchain installation!"
|
||||
|
||||
# Rebuild the client library, processor and tools with the auto-tools based
|
||||
# build system. Even though it's not going to be used, this checks that this
|
||||
# still works correctly.
|
||||
echo "Building full Android binaries with configure/make"
|
||||
TMPTARGET="$TMPDIR/target-local"
|
||||
(
|
||||
PATH="$NDK_STANDALONE/bin:$PATH"
|
||||
run mkdir "$TMPTARGET" &&
|
||||
run mkdir "$TMPDIR"/build-target &&
|
||||
run cd "$TMPDIR"/build-target &&
|
||||
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
|
||||
--host="$GNU_CONFIG" &&
|
||||
run2 make -j$NUM_JOBS install
|
||||
)
|
||||
fail_panic "Could not rebuild Android binaries!"
|
||||
|
||||
# Build and/or run unit test suite.
|
||||
# If --no-device is used, only rebuild it, otherwise, run in on the
|
||||
# connected device.
|
||||
if [ "$NO_DEVICE" ]; then
|
||||
ACTION="Building"
|
||||
# This is a trick to force the Makefile to ignore running the scripts.
|
||||
TESTS_ENVIRONMENT="TESTS_ENVIRONMENT=true"
|
||||
else
|
||||
ACTION="Running"
|
||||
TESTS_ENVIRONMENT=
|
||||
fi
|
||||
|
||||
(
|
||||
PATH="$NDK_STANDALONE/bin:$PATH"
|
||||
run cd "$TMPDIR"/build-target &&
|
||||
# Reconfigure to only run the client unit test suite.
|
||||
# This one should _never_ fail.
|
||||
dump "$ACTION Android client library unit tests."
|
||||
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
|
||||
--host="$GNU_CONFIG" \
|
||||
--disable-tools \
|
||||
--disable-processor &&
|
||||
run make -j$NUM_JOBS check $TESTS_ENVIRONMENT || exit $?
|
||||
|
||||
if [ "$ALL_TESTS" ]; then
|
||||
dump "$ACTION Tools and processor unit tests."
|
||||
# Reconfigure to run the processor and tools tests.
|
||||
# Most of these fail for now, so do not worry about it.
|
||||
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
|
||||
--host="$GNU_CONFIG" &&
|
||||
run make -j$NUM_JOBS check $TESTS_ENVIRONMENT
|
||||
if [ $? != 0 ]; then
|
||||
dump "Tools and processor unit tests failed as expected. \
|
||||
Use --verbose for results."
|
||||
fi
|
||||
fi
|
||||
)
|
||||
fail_panic "Client library unit test suite failed!"
|
||||
|
||||
# Copy sources to temporary directory
|
||||
PROJECT_DIR=$TMPDIR/project
|
||||
dump "Copying test program sources to: $PROJECT_DIR"
|
||||
run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" &&
|
||||
run rm -rf "$PROJECT_DIR/obj" &&
|
||||
run rm -rf "$PROJECT_DIR/libs"
|
||||
fail_panic "Could not copy test program sources to: $PROJECT_DIR"
|
||||
|
||||
# Build the test program with ndk-build.
|
||||
dump "Building test program with ndk-build"
|
||||
export NDK_MODULE_PATH="$PROGDIR"
|
||||
NDK_BUILD_FLAGS="-j$NUM_JOBS"
|
||||
if verbosity_is_higher_than 1; then
|
||||
NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1"
|
||||
fi
|
||||
run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI
|
||||
fail_panic "Can't build test program!"
|
||||
|
||||
# Unless --no-device was used, stop right here if ADB isn't in the path,
|
||||
# or there is no connected device.
|
||||
if [ "$NO_DEVICE" ]; then
|
||||
dump "Done. Please connect a device to run all tests!"
|
||||
clean_exit 0
|
||||
fi
|
||||
|
||||
# Push the program to the device.
|
||||
TESTAPP=test_google_breakpad
|
||||
TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad"
|
||||
if [ ! -f "$TESTAPP_FILE" ]; then
|
||||
panic "Device requires '$ABI' binaries. None found!"
|
||||
fi
|
||||
|
||||
# Run the program there
|
||||
dump "Installing test program on device"
|
||||
DEVICE_TMP=/data/local/tmp
|
||||
adb_push "$TESTAPP_FILE" "$DEVICE_TMP/"
|
||||
fail_panic "Cannot push test program to device!"
|
||||
|
||||
dump "Running test program on device"
|
||||
adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null
|
||||
if [ $? = 0 ]; then
|
||||
panic "Test program did *not* crash as expected!"
|
||||
fi
|
||||
if verbosity_is_higher_than 0; then
|
||||
echo -n "Crash log: "
|
||||
cat "$CRASH_LOG"
|
||||
fi
|
||||
|
||||
# Extract minidump from device
|
||||
MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG")
|
||||
MINIDUMP_NAME=$(basename "$MINIDUMP_NAME")
|
||||
if [ -z "$MINIDUMP_NAME" ]; then
|
||||
panic "Test program didn't write minidump properly!"
|
||||
fi
|
||||
|
||||
dump "Extracting minidump: $MINIDUMP_NAME"
|
||||
adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" .
|
||||
fail_panic "Can't extract minidump!"
|
||||
|
||||
dump "Parsing test program symbols"
|
||||
if verbosity_is_higher_than 1; then
|
||||
log "COMMAND: $TMPBIN/dump_syms \
|
||||
$PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym"
|
||||
fi
|
||||
"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym
|
||||
fail_panic "dump_syms doesn't work!"
|
||||
|
||||
VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym)
|
||||
dump "Found module version: $VERSION"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "ERROR: Can't find proper module version from symbol dump!"
|
||||
head -n5 $TESTAPP.sym
|
||||
clean_exit 1
|
||||
fi
|
||||
|
||||
run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION"
|
||||
run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/"
|
||||
|
||||
dump "Generating stack trace"
|
||||
# Don't use 'run' to be able to send stdout and stderr to two different files.
|
||||
log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols"
|
||||
"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \
|
||||
"$TMPDIR/symbols" \
|
||||
> "$BUILD_LOG" 2>>"$RUN_LOG"
|
||||
fail_panic "minidump_stackwalk doesn't work!"
|
||||
|
||||
dump "Checking stack trace content"
|
||||
|
||||
if verbosity_is_higher_than 1; then
|
||||
cat "$BUILD_LOG"
|
||||
fi
|
||||
|
||||
# The generated stack trace should look like the following:
|
||||
#
|
||||
# Thread 0 (crashed)
|
||||
# 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4]
|
||||
# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
|
||||
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
|
||||
# sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84
|
||||
# Found by: given as instruction pointer in context
|
||||
# 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3]
|
||||
# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
|
||||
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
|
||||
# sp = 0xbea2cb50 pc = 0x00009025
|
||||
# Found by: call frame info
|
||||
# 2 libc.so + 0x164e5
|
||||
# r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c
|
||||
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
|
||||
# sp = 0xbea2cc18 pc = 0x400c34e7
|
||||
# Found by: call frame info
|
||||
# ...
|
||||
#
|
||||
# The most important part for us is ensuring that the source location could
|
||||
# be extracted, so look at the 'test_breakpad.cpp' references here.
|
||||
#
|
||||
# First, extract all the lines with test_google_breakpad! in them, and
|
||||
# dump the corresponding crash location.
|
||||
#
|
||||
# Note that if the source location can't be extracted, the second field
|
||||
# will only be 'test_google_breakpad' without the exclamation mark.
|
||||
#
|
||||
LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG")
|
||||
|
||||
if [ -z "$LOCATIONS" ]; then
|
||||
if verbosity_is_lower_than 1; then
|
||||
cat "$BUILD_LOG"
|
||||
fi
|
||||
panic "No source location found in stack trace!"
|
||||
fi
|
||||
|
||||
# Now check that they all match "[<source file>"
|
||||
BAD_LOCATIONS=
|
||||
for LOCATION in $LOCATIONS; do
|
||||
case $LOCATION in
|
||||
# Escape the opening bracket, or some shells like Dash will not
|
||||
# match them properly.
|
||||
\[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable
|
||||
;;
|
||||
*) # Everything else is not!
|
||||
BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$BAD_LOCATIONS" ]; then
|
||||
dump "ERROR: Generated stack trace doesn't contain valid source locations:"
|
||||
cat "$BUILD_LOG"
|
||||
echo "Bad locations are: $BAD_LOCATIONS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All clear! Congratulations."
|
||||
|
@ -1,32 +0,0 @@
|
||||
This is a sample Android executable that can be used to test the
|
||||
Google Breakpad client library on Android.
|
||||
|
||||
Its purpose is simply to crash and generate a minidump under /data/local/tmp.
|
||||
|
||||
Build instructions:
|
||||
|
||||
cd android/sample_app
|
||||
$NDK/ndk-build
|
||||
|
||||
Where $NDK points to a valid Android NDK installation.
|
||||
|
||||
Usage instructions:
|
||||
|
||||
After buildind the test program, send it to a device, then run it as
|
||||
the shell UID:
|
||||
|
||||
adb push libs/armeabi/test_google_breakpad /data/local/tmp
|
||||
adb shell /data/local/tmp/test_google_breakpad
|
||||
|
||||
This will simply crash after dumping the name of the generated minidump
|
||||
file.
|
||||
|
||||
See jni/test_breakpad.cpp for details.
|
||||
|
||||
Use 'armeabi-v7a' instead of 'armeabi' above to test the ARMv7-A version
|
||||
of the binary.
|
||||
|
||||
Note:
|
||||
If you plan to use the library in a regular Android application, store
|
||||
the minidump files either to your app-specific directory, or to the SDCard
|
||||
(the latter requiring a specific permission).
|
@ -1,44 +0,0 @@
|
||||
# Copyright (c) 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := test_google_breakpad
|
||||
LOCAL_SRC_FILES := test_breakpad.cpp
|
||||
LOCAL_STATIC_LIBRARIES += breakpad_client
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# If NDK_MODULE_PATH is defined, import the module, otherwise do a direct
|
||||
# includes. This allows us to build in all scenarios easily.
|
||||
ifneq ($(NDK_MODULE_PATH),)
|
||||
$(call import-module,google_breakpad)
|
||||
else
|
||||
include $(LOCAL_PATH)/../../google_breakpad/Android.mk
|
||||
endif
|
@ -1,32 +0,0 @@
|
||||
# Copyright (c) 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
APP_STL := stlport_static
|
||||
APP_ABI := all
|
||||
APP_CXXFLAGS := -std=c++11 -D__STDC_LIMIT_MACROS
|
@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
|
||||
namespace {
|
||||
|
||||
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 = reinterpret_cast<volatile int*>(NULL);
|
||||
*a = 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
google_breakpad::MinidumpDescriptor descriptor(".");
|
||||
google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback,
|
||||
NULL, true, -1);
|
||||
Crash();
|
||||
return 0;
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
# Slightly modified for Android, see ANDROID comment below.
|
||||
|
||||
scriptversion=2012-06-27.10; # UTC
|
||||
|
||||
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--] TEST-SCRIPT
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
# TODO: better error handling in option parsing (in particular, ensure
|
||||
# TODO: $log_file, $trs_file and $test_name are defined).
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
# ANDROID: old line was: "$@" > $log_file 2>&1
|
||||
progdir=$(dirname "$0")
|
||||
"$progdir/test-shell.sh" "$@" > $log_file 2>&1
|
||||
estatus=$?
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
estatus=1
|
||||
fi
|
||||
|
||||
case $estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
@ -1,131 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2012 Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# A special shell wrapper that can be used to run the Google Breakpad unit
|
||||
# tests on a connected Android device.
|
||||
#
|
||||
# This is designed to be called from the Makefile during 'make check'
|
||||
#
|
||||
|
||||
PROGDIR=$(dirname "$0")
|
||||
PROGNAME=$(basename "$0")
|
||||
. $PROGDIR/common-functions.sh
|
||||
|
||||
# Extract test program name first.
|
||||
TEST_PROGRAM=$1
|
||||
shift
|
||||
|
||||
if [ -z "$TEST_PROGRAM" ]; then
|
||||
panic "No test program/script name on the command-line!"
|
||||
fi
|
||||
|
||||
if [ ! -f "$TEST_PROGRAM" ]; then
|
||||
panic "Can't find test program/script: $TEST_PROGRAM"
|
||||
fi
|
||||
|
||||
# Create test directory on the device
|
||||
TEST_DIR=/data/local/tmp/test-google-breakpad-$$
|
||||
adb_shell mkdir "$TEST_DIR" ||
|
||||
panic "Can't create test directory on device: $TEST_DIR"
|
||||
|
||||
# Ensure that it is always removed when the script exits.
|
||||
clean_test_dir () {
|
||||
# Don't care about success/failure, use '$ADB shell' directly.
|
||||
adb_shell rm -r "$TEST_DIR"
|
||||
}
|
||||
|
||||
atexit clean_test_dir
|
||||
|
||||
TEST_PROGRAM_NAME=$(basename "$TEST_PROGRAM")
|
||||
TEST_PROGRAM_DIR=$(dirname "$TEST_PROGRAM")
|
||||
|
||||
# Handle special case(s) here.
|
||||
DATA_FILES=
|
||||
case $TEST_PROGRAM_NAME in
|
||||
linux_client_unittest)
|
||||
# linux_client_unittest will call another executable at runtime, ensure
|
||||
# it is installed too.
|
||||
adb_install "$TEST_PROGRAM_DIR/linux_dumper_unittest_helper" "$TEST_DIR"
|
||||
# linux_client_unittest loads a shared library at runtime, ensure it is
|
||||
# installed too.
|
||||
adb_install "$TEST_PROGRAM_DIR/linux_client_unittest_shlib" "$TEST_DIR"
|
||||
;;
|
||||
basic_source_line_resolver_unittest)
|
||||
DATA_FILES="module1.out \
|
||||
module2.out \
|
||||
module3_bad.out \
|
||||
module4_bad.out"
|
||||
;;
|
||||
exploitability_unittest)
|
||||
DATA_FILES="scii_read_av.dmp \
|
||||
ascii_read_av_block_write.dmp \
|
||||
ascii_read_av_clobber_write.dmp \
|
||||
ascii_read_av_conditional.dmp \
|
||||
ascii_read_av_non_null.dmp \
|
||||
ascii_read_av_then_jmp.dmp \
|
||||
ascii_read_av_xchg_write.dmp \
|
||||
ascii_write_av.dmp \
|
||||
ascii_write_av_arg_to_call.dmp \
|
||||
exec_av_on_stack.dmp \
|
||||
null_read_av.dmp \
|
||||
null_write_av.dmp \
|
||||
read_av.dmp \
|
||||
null_read_av.dmp \
|
||||
write_av_non_null.dmp"
|
||||
;;
|
||||
fast_source_line_resolver_unittest)
|
||||
DATA_FILES="module0.out \
|
||||
module1.out \
|
||||
module2.out \
|
||||
module3_bad.out \
|
||||
module4_bad.out"
|
||||
;;
|
||||
minidump_processor_unittest|minidump_unittest)
|
||||
DATA_FILES="src/processor/testdata/minidump2.dmp"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install the data files, their path is relative to the environment
|
||||
# variable 'srcdir'
|
||||
for FILE in $DATA_FILES; do
|
||||
FILEDIR=src/processor/testdata/$(dirname "$FILE")
|
||||
adb_shell mkdir -p "$TEST_DIR/$FILEDIR"
|
||||
adb_install "${srcdir:-.}/$FILE" "$TEST_DIR"/"$FILE"
|
||||
done
|
||||
|
||||
# Copy test program to device
|
||||
adb_install "$TEST_PROGRAM" "$TEST_DIR"
|
||||
|
||||
# Run it
|
||||
adb_shell "cd $TEST_DIR && LD_LIBRARY_PATH=. ./$TEST_PROGRAM_NAME $@"
|
||||
|
||||
# Note: exiting here will call cleanup_exit which will remove the temporary
|
||||
# files from the device.
|
270
autotools/ar-lib
270
autotools/ar-lib
@ -1,270 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for Microsoft lib.exe
|
||||
|
||||
me=ar-lib
|
||||
scriptversion=2012-03-01.08; # UTC
|
||||
|
||||
# Copyright (C) 2010-2014 Free Software Foundation, Inc.
|
||||
# Written by Peter Rosin <peda@lysator.liu.se>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
|
||||
# func_error message
|
||||
func_error ()
|
||||
{
|
||||
echo "$me: $1" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv in
|
||||
mingw)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_at_file at_file operation archive
|
||||
# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
|
||||
# for each of them.
|
||||
# When interpreting the content of the @FILE, do NOT use func_file_conv,
|
||||
# since the user would need to supply preconverted file names to
|
||||
# binutils ar, at least for MinGW.
|
||||
func_at_file ()
|
||||
{
|
||||
operation=$2
|
||||
archive=$3
|
||||
at_file_contents=`cat "$1"`
|
||||
eval set x "$at_file_contents"
|
||||
shift
|
||||
|
||||
for member
|
||||
do
|
||||
$AR -NOLOGO $operation:"$member" "$archive" || exit $?
|
||||
done
|
||||
}
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
func_error "no command. Try '$0 --help' for more information."
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<EOF
|
||||
Usage: $me [--help] [--version] PROGRAM ACTION ARCHIVE [MEMBER...]
|
||||
|
||||
Members may be specified in a file named with @FILE.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "$me, version $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
if test $# -lt 3; then
|
||||
func_error "you must specify a program, an action and an archive"
|
||||
fi
|
||||
|
||||
AR=$1
|
||||
shift
|
||||
while :
|
||||
do
|
||||
if test $# -lt 2; then
|
||||
func_error "you must specify a program, an action and an archive"
|
||||
fi
|
||||
case $1 in
|
||||
-lib | -LIB \
|
||||
| -ltcg | -LTCG \
|
||||
| -machine* | -MACHINE* \
|
||||
| -subsystem* | -SUBSYSTEM* \
|
||||
| -verbose | -VERBOSE \
|
||||
| -wx* | -WX* )
|
||||
AR="$AR $1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
action=$1
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
orig_archive=$1
|
||||
shift
|
||||
func_file_conv "$orig_archive"
|
||||
archive=$file
|
||||
|
||||
# strip leading dash in $action
|
||||
action=${action#-}
|
||||
|
||||
delete=
|
||||
extract=
|
||||
list=
|
||||
quick=
|
||||
replace=
|
||||
index=
|
||||
create=
|
||||
|
||||
while test -n "$action"
|
||||
do
|
||||
case $action in
|
||||
d*) delete=yes ;;
|
||||
x*) extract=yes ;;
|
||||
t*) list=yes ;;
|
||||
q*) quick=yes ;;
|
||||
r*) replace=yes ;;
|
||||
s*) index=yes ;;
|
||||
S*) ;; # the index is always updated implicitly
|
||||
c*) create=yes ;;
|
||||
u*) ;; # TODO: don't ignore the update modifier
|
||||
v*) ;; # TODO: don't ignore the verbose modifier
|
||||
*)
|
||||
func_error "unknown action specified"
|
||||
;;
|
||||
esac
|
||||
action=${action#?}
|
||||
done
|
||||
|
||||
case $delete$extract$list$quick$replace,$index in
|
||||
yes,* | ,yes)
|
||||
;;
|
||||
yesyes*)
|
||||
func_error "more than one action specified"
|
||||
;;
|
||||
*)
|
||||
func_error "no action specified"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$delete"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_at_file "${1#@}" -REMOVE "$archive"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
$AR -NOLOGO -REMOVE:"$file" "$archive" || exit $?
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
elif test -n "$extract"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
if test $# -gt 0; then
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_at_file "${1#@}" -EXTRACT "$archive"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
$AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $?
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
$AR -NOLOGO -LIST "$archive" | sed -e 's/\\/\\\\/g' | while read member
|
||||
do
|
||||
$AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
|
||||
done
|
||||
fi
|
||||
|
||||
elif test -n "$quick$replace"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
if test -z "$create"; then
|
||||
echo "$me: creating $orig_archive"
|
||||
fi
|
||||
orig_archive=
|
||||
else
|
||||
orig_archive=$archive
|
||||
fi
|
||||
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_file_conv "${1#@}"
|
||||
set x "$@" "@$file"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
set x "$@" "$file"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
shift
|
||||
done
|
||||
|
||||
if test -n "$orig_archive"; then
|
||||
$AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $?
|
||||
else
|
||||
$AR -NOLOGO -OUT:"$archive" "$@" || exit $?
|
||||
fi
|
||||
|
||||
elif test -n "$list"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
$AR -NOLOGO -LIST "$archive" || exit $?
|
||||
fi
|
@ -1,347 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-10-14.11; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/lib$lib.a"; then
|
||||
found=yes
|
||||
lib=$dir/lib$lib.a
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
1
autotools/compile
Symbolic link
1
autotools/compile
Symbolic link
@ -0,0 +1 @@
|
||||
/usr/share/automake-1.11/compile
|
851
autotools/config.guess
vendored
851
autotools/config.guess
vendored
File diff suppressed because it is too large
Load Diff
415
autotools/config.sub
vendored
415
autotools/config.sub
vendored
@ -1,40 +1,44 @@
|
||||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
|
||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
timestamp='2016-01-01'
|
||||
timestamp='2006-09-20'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# This file is (in principle) common to ALL GNU software.
|
||||
# The presence of a machine in this file suggests that SOME GNU software
|
||||
# can handle that machine. It does not imply ALL GNU software can.
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that
|
||||
# program. This Exception is an additional permission under section 7
|
||||
# of the GNU General Public License, version 3 ("GPLv3").
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
|
||||
# Please send patches to <config-patches@gnu.org>.
|
||||
# Please send patches to <config-patches@gnu.org>. Submit a context
|
||||
# diff and a properly formatted ChangeLog entry.
|
||||
#
|
||||
# Configuration subroutine to validate and canonicalize a configuration type.
|
||||
# Supply the specified configuration type as an argument.
|
||||
# If it is invalid, we print an error message on stderr and exit with code 1.
|
||||
# Otherwise, we print the canonical config type on stdout and succeed.
|
||||
|
||||
# You can get the latest version of this script from:
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
|
||||
|
||||
# This file is supposed to be the same for all GNU packages
|
||||
# and recognize all the CPU types, system types and aliases
|
||||
# that are meaningful with *any* GNU software.
|
||||
@ -53,7 +57,8 @@ timestamp='2016-01-01'
|
||||
me=`echo "$0" | sed -e 's,.*/,,'`
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
|
||||
Usage: $0 [OPTION] CPU-MFR-OPSYS
|
||||
$0 [OPTION] ALIAS
|
||||
|
||||
Canonicalize a configuration name.
|
||||
|
||||
@ -67,7 +72,8 @@ Report bugs and patches to <config-patches@gnu.org>."
|
||||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@ -114,18 +120,12 @@ esac
|
||||
# Here we must recognize all the valid KERNEL-OS combinations.
|
||||
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
||||
case $maybe_os in
|
||||
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
||||
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
||||
knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
|
||||
kopensolaris*-gnu* | \
|
||||
nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
|
||||
uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
|
||||
storm-chaos* | os2-emx* | rtmk-nova*)
|
||||
os=-$maybe_os
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
|
||||
;;
|
||||
android-linux)
|
||||
os=-linux-android
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
|
||||
;;
|
||||
*)
|
||||
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
|
||||
if [ $basic_machine != $1 ]
|
||||
@ -148,13 +148,10 @@ case $os in
|
||||
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
|
||||
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
|
||||
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
|
||||
-apple | -axis | -knuth | -cray | -microblaze*)
|
||||
-apple | -axis | -knuth | -cray)
|
||||
os=
|
||||
basic_machine=$1
|
||||
;;
|
||||
-bluegene*)
|
||||
os=-cnk
|
||||
;;
|
||||
-sim | -cisco | -oki | -wec | -winbond)
|
||||
os=
|
||||
basic_machine=$1
|
||||
@ -169,10 +166,10 @@ case $os in
|
||||
os=-chorusos
|
||||
basic_machine=$1
|
||||
;;
|
||||
-chorusrdb)
|
||||
os=-chorusrdb
|
||||
-chorusrdb)
|
||||
os=-chorusrdb
|
||||
basic_machine=$1
|
||||
;;
|
||||
;;
|
||||
-hiux*)
|
||||
os=-hiuxwe2
|
||||
;;
|
||||
@ -217,12 +214,6 @@ case $os in
|
||||
-isc*)
|
||||
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
|
||||
;;
|
||||
-lynx*178)
|
||||
os=-lynxos178
|
||||
;;
|
||||
-lynx*5)
|
||||
os=-lynxos5
|
||||
;;
|
||||
-lynx*)
|
||||
os=-lynxos
|
||||
;;
|
||||
@ -247,91 +238,59 @@ case $basic_machine in
|
||||
# Some are omitted here because they have special meanings below.
|
||||
1750a | 580 \
|
||||
| a29k \
|
||||
| aarch64 | aarch64_be \
|
||||
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
|
||||
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
|
||||
| am33_2.0 \
|
||||
| arc | arceb \
|
||||
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
|
||||
| avr | avr32 \
|
||||
| ba \
|
||||
| be32 | be64 \
|
||||
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
|
||||
| bfin \
|
||||
| c4x | c8051 | clipper \
|
||||
| c4x | clipper \
|
||||
| d10v | d30v | dlx | dsp16xx \
|
||||
| e2k | epiphany \
|
||||
| fido | fr30 | frv | ft32 \
|
||||
| fr30 | frv \
|
||||
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
||||
| hexagon \
|
||||
| i370 | i860 | i960 | ia64 \
|
||||
| ip2k | iq2000 \
|
||||
| k1om \
|
||||
| le32 | le64 \
|
||||
| lm32 \
|
||||
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
||||
| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
|
||||
| maxq | mb | microblaze | mcore \
|
||||
| mips | mipsbe | mipseb | mipsel | mipsle \
|
||||
| mips16 \
|
||||
| mips64 | mips64el \
|
||||
| mips64octeon | mips64octeonel \
|
||||
| mips64orion | mips64orionel \
|
||||
| mips64r5900 | mips64r5900el \
|
||||
| mips64vr | mips64vrel \
|
||||
| mips64orion | mips64orionel \
|
||||
| mips64vr4100 | mips64vr4100el \
|
||||
| mips64vr4300 | mips64vr4300el \
|
||||
| mips64vr5000 | mips64vr5000el \
|
||||
| mips64vr5900 | mips64vr5900el \
|
||||
| mipsisa32 | mipsisa32el \
|
||||
| mipsisa32r2 | mipsisa32r2el \
|
||||
| mipsisa32r6 | mipsisa32r6el \
|
||||
| mipsisa64 | mipsisa64el \
|
||||
| mipsisa64r2 | mipsisa64r2el \
|
||||
| mipsisa64r6 | mipsisa64r6el \
|
||||
| mipsisa64sb1 | mipsisa64sb1el \
|
||||
| mipsisa64sr71k | mipsisa64sr71kel \
|
||||
| mipsr5900 | mipsr5900el \
|
||||
| mipstx39 | mipstx39el \
|
||||
| mn10200 | mn10300 \
|
||||
| moxie \
|
||||
| mt \
|
||||
| msp430 \
|
||||
| nds32 | nds32le | nds32be \
|
||||
| nios | nios2 | nios2eb | nios2el \
|
||||
| nios | nios2 \
|
||||
| ns16k | ns32k \
|
||||
| open8 | or1k | or1knd | or32 \
|
||||
| or32 \
|
||||
| pdp10 | pdp11 | pj | pjl \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
|
||||
| pyramid \
|
||||
| riscv32 | riscv64 \
|
||||
| rl78 | rx \
|
||||
| score \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh64 | sh64le \
|
||||
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
|
||||
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
|
||||
| spu \
|
||||
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
|
||||
| ubicom32 \
|
||||
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
|
||||
| visium \
|
||||
| spu | strongarm \
|
||||
| tahoe | thumb | tic4x | tic80 | tron \
|
||||
| v850 | v850e \
|
||||
| we32k \
|
||||
| x86 | xc16x | xstormy16 | xtensa \
|
||||
| z8k | z80)
|
||||
| x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
|
||||
| z8k)
|
||||
basic_machine=$basic_machine-unknown
|
||||
;;
|
||||
c54x)
|
||||
basic_machine=tic54x-unknown
|
||||
;;
|
||||
c55x)
|
||||
basic_machine=tic55x-unknown
|
||||
;;
|
||||
c6x)
|
||||
basic_machine=tic6x-unknown
|
||||
;;
|
||||
leon|leon[3-9])
|
||||
basic_machine=sparc-$basic_machine
|
||||
;;
|
||||
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
|
||||
m6811 | m68hc11 | m6812 | m68hc12)
|
||||
# Motorola 68HC11/12.
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
;;
|
||||
@ -341,21 +300,6 @@ case $basic_machine in
|
||||
basic_machine=mt-unknown
|
||||
;;
|
||||
|
||||
strongarm | thumb | xscale)
|
||||
basic_machine=arm-unknown
|
||||
;;
|
||||
xgate)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
;;
|
||||
xscaleeb)
|
||||
basic_machine=armeb-unknown
|
||||
;;
|
||||
|
||||
xscaleel)
|
||||
basic_machine=armel-unknown
|
||||
;;
|
||||
|
||||
# We use `pc' rather than `unknown'
|
||||
# because (1) that's what they normally are, and
|
||||
# (2) the word "unknown" tends to confuse beginning users.
|
||||
@ -370,89 +314,64 @@ case $basic_machine in
|
||||
# Recognize the basic CPU types with company name.
|
||||
580-* \
|
||||
| a29k-* \
|
||||
| aarch64-* | aarch64_be-* \
|
||||
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
|
||||
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
|
||||
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
||||
| avr-* | avr32-* \
|
||||
| ba-* \
|
||||
| be32-* | be64-* \
|
||||
| bfin-* | bs2000-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
||||
| c8051-* | clipper-* | craynv-* | cydra-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
|
||||
| clipper-* | craynv-* | cydra-* \
|
||||
| d10v-* | d30v-* | dlx-* \
|
||||
| e2k-* | elxsi-* \
|
||||
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
||||
| elxsi-* \
|
||||
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
|
||||
| h8300-* | h8500-* \
|
||||
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
|
||||
| hexagon-* \
|
||||
| i*86-* | i860-* | i960-* | ia64-* \
|
||||
| ip2k-* | iq2000-* \
|
||||
| k1om-* \
|
||||
| le32-* | le64-* \
|
||||
| lm32-* \
|
||||
| m32c-* | m32r-* | m32rle-* \
|
||||
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
||||
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
|
||||
| microblaze-* | microblazeel-* \
|
||||
| m88110-* | m88k-* | maxq-* | mcore-* \
|
||||
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
|
||||
| mips16-* \
|
||||
| mips64-* | mips64el-* \
|
||||
| mips64octeon-* | mips64octeonel-* \
|
||||
| mips64orion-* | mips64orionel-* \
|
||||
| mips64r5900-* | mips64r5900el-* \
|
||||
| mips64vr-* | mips64vrel-* \
|
||||
| mips64orion-* | mips64orionel-* \
|
||||
| mips64vr4100-* | mips64vr4100el-* \
|
||||
| mips64vr4300-* | mips64vr4300el-* \
|
||||
| mips64vr5000-* | mips64vr5000el-* \
|
||||
| mips64vr5900-* | mips64vr5900el-* \
|
||||
| mipsisa32-* | mipsisa32el-* \
|
||||
| mipsisa32r2-* | mipsisa32r2el-* \
|
||||
| mipsisa32r6-* | mipsisa32r6el-* \
|
||||
| mipsisa64-* | mipsisa64el-* \
|
||||
| mipsisa64r2-* | mipsisa64r2el-* \
|
||||
| mipsisa64r6-* | mipsisa64r6el-* \
|
||||
| mipsisa64sb1-* | mipsisa64sb1el-* \
|
||||
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
|
||||
| mipsr5900-* | mipsr5900el-* \
|
||||
| mipstx39-* | mipstx39el-* \
|
||||
| mmix-* \
|
||||
| mt-* \
|
||||
| msp430-* \
|
||||
| nds32-* | nds32le-* | nds32be-* \
|
||||
| nios-* | nios2-* | nios2eb-* | nios2el-* \
|
||||
| nios-* | nios2-* \
|
||||
| none-* | np1-* | ns16k-* | ns32k-* \
|
||||
| open8-* \
|
||||
| or1k*-* \
|
||||
| orion-* \
|
||||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
|
||||
| pyramid-* \
|
||||
| riscv32-* | riscv64-* \
|
||||
| rl78-* | romp-* | rs6000-* | rx-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| romp-* | rs6000-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
|
||||
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
|
||||
| sparclite-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
|
||||
| tahoe-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
|
||||
| tahoe-* | thumb-* \
|
||||
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
|
||||
| tile*-* \
|
||||
| tron-* \
|
||||
| ubicom32-* \
|
||||
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
|
||||
| vax-* \
|
||||
| visium-* \
|
||||
| v850-* | v850e-* | vax-* \
|
||||
| we32k-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* \
|
||||
| xstormy16-* | xtensa*-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
|
||||
| xstormy16-* | xtensa-* \
|
||||
| ymp-* \
|
||||
| z8k-* | z80-*)
|
||||
;;
|
||||
# Recognize the basic CPU types without company name, with glob match.
|
||||
xtensa*)
|
||||
basic_machine=$basic_machine-unknown
|
||||
| z8k-*)
|
||||
;;
|
||||
# Recognize the various machine names and aliases which stand
|
||||
# for a CPU type and a company and sometimes even an OS.
|
||||
@ -470,7 +389,7 @@ case $basic_machine in
|
||||
basic_machine=a29k-amd
|
||||
os=-udi
|
||||
;;
|
||||
abacus)
|
||||
abacus)
|
||||
basic_machine=abacus-unknown
|
||||
;;
|
||||
adobe68k)
|
||||
@ -516,13 +435,6 @@ case $basic_machine in
|
||||
basic_machine=m68k-apollo
|
||||
os=-bsd
|
||||
;;
|
||||
aros)
|
||||
basic_machine=i386-pc
|
||||
os=-aros
|
||||
;;
|
||||
asmjs)
|
||||
basic_machine=asmjs-unknown
|
||||
;;
|
||||
aux)
|
||||
basic_machine=m68k-apple
|
||||
os=-aux
|
||||
@ -531,35 +443,10 @@ case $basic_machine in
|
||||
basic_machine=ns32k-sequent
|
||||
os=-dynix
|
||||
;;
|
||||
blackfin)
|
||||
basic_machine=bfin-unknown
|
||||
os=-linux
|
||||
;;
|
||||
blackfin-*)
|
||||
basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
bluegene*)
|
||||
basic_machine=powerpc-ibm
|
||||
os=-cnk
|
||||
;;
|
||||
c54x-*)
|
||||
basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c55x-*)
|
||||
basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c6x-*)
|
||||
basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c90)
|
||||
basic_machine=c90-cray
|
||||
os=-unicos
|
||||
;;
|
||||
cegcc)
|
||||
basic_machine=arm-unknown
|
||||
os=-cegcc
|
||||
;;
|
||||
convex-c1)
|
||||
basic_machine=c1-convex
|
||||
os=-bsd
|
||||
@ -588,8 +475,8 @@ case $basic_machine in
|
||||
basic_machine=craynv-cray
|
||||
os=-unicosmp
|
||||
;;
|
||||
cr16 | cr16-*)
|
||||
basic_machine=cr16-unknown
|
||||
cr16c)
|
||||
basic_machine=cr16c-unknown
|
||||
os=-elf
|
||||
;;
|
||||
crds | unos)
|
||||
@ -627,10 +514,6 @@ case $basic_machine in
|
||||
basic_machine=m88k-motorola
|
||||
os=-sysv3
|
||||
;;
|
||||
dicos)
|
||||
basic_machine=i686-pc
|
||||
os=-dicos
|
||||
;;
|
||||
djgpp)
|
||||
basic_machine=i586-pc
|
||||
os=-msdosdjgpp
|
||||
@ -746,6 +629,7 @@ case $basic_machine in
|
||||
i370-ibm* | ibm*)
|
||||
basic_machine=i370-ibm
|
||||
;;
|
||||
# I'm not sure what "Sysv32" means. Should this be sysv3.2?
|
||||
i*86v32)
|
||||
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
|
||||
os=-sysv32
|
||||
@ -784,17 +668,6 @@ case $basic_machine in
|
||||
basic_machine=m68k-isi
|
||||
os=-sysv
|
||||
;;
|
||||
leon-*|leon[3-9]-*)
|
||||
basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
|
||||
;;
|
||||
m68knommu)
|
||||
basic_machine=m68k-unknown
|
||||
os=-linux
|
||||
;;
|
||||
m68knommu-*)
|
||||
basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
m88k-omron*)
|
||||
basic_machine=m88k-omron
|
||||
;;
|
||||
@ -806,21 +679,10 @@ case $basic_machine in
|
||||
basic_machine=ns32k-utek
|
||||
os=-sysv
|
||||
;;
|
||||
microblaze*)
|
||||
basic_machine=microblaze-xilinx
|
||||
;;
|
||||
mingw64)
|
||||
basic_machine=x86_64-pc
|
||||
os=-mingw64
|
||||
;;
|
||||
mingw32)
|
||||
basic_machine=i686-pc
|
||||
basic_machine=i386-pc
|
||||
os=-mingw32
|
||||
;;
|
||||
mingw32ce)
|
||||
basic_machine=arm-unknown
|
||||
os=-mingw32ce
|
||||
;;
|
||||
miniframe)
|
||||
basic_machine=m68000-convergent
|
||||
;;
|
||||
@ -842,10 +704,6 @@ case $basic_machine in
|
||||
basic_machine=powerpc-unknown
|
||||
os=-morphos
|
||||
;;
|
||||
moxiebox)
|
||||
basic_machine=moxie-unknown
|
||||
os=-moxiebox
|
||||
;;
|
||||
msdos)
|
||||
basic_machine=i386-pc
|
||||
os=-msdos
|
||||
@ -853,18 +711,10 @@ case $basic_machine in
|
||||
ms1-*)
|
||||
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
|
||||
;;
|
||||
msys)
|
||||
basic_machine=i686-pc
|
||||
os=-msys
|
||||
;;
|
||||
mvs)
|
||||
basic_machine=i370-ibm
|
||||
os=-mvs
|
||||
;;
|
||||
nacl)
|
||||
basic_machine=le32-unknown
|
||||
os=-nacl
|
||||
;;
|
||||
ncr3000)
|
||||
basic_machine=i486-ncr
|
||||
os=-sysv4
|
||||
@ -929,12 +779,6 @@ case $basic_machine in
|
||||
np1)
|
||||
basic_machine=np1-gould
|
||||
;;
|
||||
neo-tandem)
|
||||
basic_machine=neo-tandem
|
||||
;;
|
||||
nse-tandem)
|
||||
basic_machine=nse-tandem
|
||||
;;
|
||||
nsr-tandem)
|
||||
basic_machine=nsr-tandem
|
||||
;;
|
||||
@ -965,14 +809,6 @@ case $basic_machine in
|
||||
basic_machine=i860-intel
|
||||
os=-osf
|
||||
;;
|
||||
parisc)
|
||||
basic_machine=hppa-unknown
|
||||
os=-linux
|
||||
;;
|
||||
parisc-*)
|
||||
basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
pbd)
|
||||
basic_machine=sparc-tti
|
||||
;;
|
||||
@ -1017,10 +853,9 @@ case $basic_machine in
|
||||
;;
|
||||
power) basic_machine=power-ibm
|
||||
;;
|
||||
ppc | ppcbe) basic_machine=powerpc-unknown
|
||||
ppc) basic_machine=powerpc-unknown
|
||||
;;
|
||||
ppc-* | ppcbe-*)
|
||||
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
ppcle | powerpclittle | ppc-le | powerpc-little)
|
||||
basic_machine=powerpcle-unknown
|
||||
@ -1045,11 +880,7 @@ case $basic_machine in
|
||||
basic_machine=i586-unknown
|
||||
os=-pw32
|
||||
;;
|
||||
rdos | rdos64)
|
||||
basic_machine=x86_64-pc
|
||||
os=-rdos
|
||||
;;
|
||||
rdos32)
|
||||
rdos)
|
||||
basic_machine=i386-pc
|
||||
os=-rdos
|
||||
;;
|
||||
@ -1094,9 +925,6 @@ case $basic_machine in
|
||||
basic_machine=sh-hitachi
|
||||
os=-hms
|
||||
;;
|
||||
sh5el)
|
||||
basic_machine=sh5le-unknown
|
||||
;;
|
||||
sh64)
|
||||
basic_machine=sh64-unknown
|
||||
;;
|
||||
@ -1118,9 +946,6 @@ case $basic_machine in
|
||||
basic_machine=i860-stratus
|
||||
os=-sysv4
|
||||
;;
|
||||
strongarm-* | thumb-*)
|
||||
basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
sun2)
|
||||
basic_machine=m68000-sun
|
||||
;;
|
||||
@ -1177,9 +1002,17 @@ case $basic_machine in
|
||||
basic_machine=t90-cray
|
||||
os=-unicos
|
||||
;;
|
||||
tile*)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-linux-gnu
|
||||
tic54x | c54x*)
|
||||
basic_machine=tic54x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tic55x | c55x*)
|
||||
basic_machine=tic55x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tic6x | c6x*)
|
||||
basic_machine=tic6x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tx39)
|
||||
basic_machine=mipstx39-unknown
|
||||
@ -1248,9 +1081,6 @@ case $basic_machine in
|
||||
xps | xps100)
|
||||
basic_machine=xps100-honeywell
|
||||
;;
|
||||
xscale-* | xscalee[bl]-*)
|
||||
basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
|
||||
;;
|
||||
ymp)
|
||||
basic_machine=ymp-cray
|
||||
os=-unicos
|
||||
@ -1259,10 +1089,6 @@ case $basic_machine in
|
||||
basic_machine=z8k-unknown
|
||||
os=-sim
|
||||
;;
|
||||
z80-*-coff)
|
||||
basic_machine=z80-unknown
|
||||
os=-sim
|
||||
;;
|
||||
none)
|
||||
basic_machine=none-none
|
||||
os=-none
|
||||
@ -1301,7 +1127,7 @@ case $basic_machine in
|
||||
we32k)
|
||||
basic_machine=we32k-att
|
||||
;;
|
||||
sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
|
||||
sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
|
||||
basic_machine=sh-unknown
|
||||
;;
|
||||
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
|
||||
@ -1348,12 +1174,9 @@ esac
|
||||
if [ x"$os" != x"" ]
|
||||
then
|
||||
case $os in
|
||||
# First match some system type aliases
|
||||
# that might get confused with valid system types.
|
||||
# First match some system type aliases
|
||||
# that might get confused with valid system types.
|
||||
# -solaris* is a basic system type, with this one exception.
|
||||
-auroraux)
|
||||
os=-auroraux
|
||||
;;
|
||||
-solaris1 | -solaris1.*)
|
||||
os=`echo $os | sed -e 's|solaris1|sunos4|'`
|
||||
;;
|
||||
@ -1374,32 +1197,29 @@ case $os in
|
||||
# Each alternative MUST END IN A *, to match a version number.
|
||||
# -sysv* is not here because it comes later, after sysvr4.
|
||||
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
|
||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
||||
| -sym* | -kopensolaris* | -plan9* \
|
||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
|
||||
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
||||
| -aos* | -aros* | -cloudabi* | -sortix* \
|
||||
| -aos* \
|
||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
|
||||
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
|
||||
| -bitrig* | -openbsd* | -solidbsd* \
|
||||
| -openbsd* | -solidbsd* \
|
||||
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
|
||||
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
|
||||
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
|
||||
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
||||
| -chorusos* | -chorusrdb* | -cegcc* \
|
||||
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||
| -chorusos* | -chorusrdb* \
|
||||
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* \
|
||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
|
||||
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
|
||||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
|
||||
| -onefs* | -tirtos*)
|
||||
| -skyos* | -haiku* | -rdos* | -toppers*)
|
||||
# Remember, each alternative MUST END IN *, to match a version number.
|
||||
;;
|
||||
-qnx*)
|
||||
@ -1438,7 +1258,7 @@ case $os in
|
||||
-opened*)
|
||||
os=-openedition
|
||||
;;
|
||||
-os400*)
|
||||
-os400*)
|
||||
os=-os400
|
||||
;;
|
||||
-wince*)
|
||||
@ -1487,7 +1307,7 @@ case $os in
|
||||
-sinix*)
|
||||
os=-sysv4
|
||||
;;
|
||||
-tpf*)
|
||||
-tpf*)
|
||||
os=-tpf
|
||||
;;
|
||||
-triton*)
|
||||
@ -1523,14 +1343,12 @@ case $os in
|
||||
-aros*)
|
||||
os=-aros
|
||||
;;
|
||||
-kaos*)
|
||||
os=-kaos
|
||||
;;
|
||||
-zvmoe)
|
||||
os=-zvmoe
|
||||
;;
|
||||
-dicos*)
|
||||
os=-dicos
|
||||
;;
|
||||
-nacl*)
|
||||
;;
|
||||
-none)
|
||||
;;
|
||||
*)
|
||||
@ -1553,10 +1371,10 @@ else
|
||||
# system, and we'll never get to this point.
|
||||
|
||||
case $basic_machine in
|
||||
score-*)
|
||||
score-*)
|
||||
os=-elf
|
||||
;;
|
||||
spu-*)
|
||||
spu-*)
|
||||
os=-elf
|
||||
;;
|
||||
*-acorn)
|
||||
@ -1568,23 +1386,8 @@ case $basic_machine in
|
||||
arm*-semi)
|
||||
os=-aout
|
||||
;;
|
||||
c4x-* | tic4x-*)
|
||||
os=-coff
|
||||
;;
|
||||
c8051-*)
|
||||
os=-elf
|
||||
;;
|
||||
hexagon-*)
|
||||
os=-elf
|
||||
;;
|
||||
tic54x-*)
|
||||
os=-coff
|
||||
;;
|
||||
tic55x-*)
|
||||
os=-coff
|
||||
;;
|
||||
tic6x-*)
|
||||
os=-coff
|
||||
c4x-* | tic4x-*)
|
||||
os=-coff
|
||||
;;
|
||||
# This must come before the *-dec entry.
|
||||
pdp10-*)
|
||||
@ -1604,13 +1407,13 @@ case $basic_machine in
|
||||
;;
|
||||
m68000-sun)
|
||||
os=-sunos3
|
||||
# This also exists in the configure program, but was not the
|
||||
# default.
|
||||
# os=-sunos4
|
||||
;;
|
||||
m68*-cisco)
|
||||
os=-aout
|
||||
;;
|
||||
mep-*)
|
||||
os=-elf
|
||||
;;
|
||||
mips*-cisco)
|
||||
os=-elf
|
||||
;;
|
||||
@ -1635,7 +1438,7 @@ case $basic_machine in
|
||||
*-ibm)
|
||||
os=-aix
|
||||
;;
|
||||
*-knuth)
|
||||
*-knuth)
|
||||
os=-mmixware
|
||||
;;
|
||||
*-wec)
|
||||
@ -1740,7 +1543,7 @@ case $basic_machine in
|
||||
-sunos*)
|
||||
vendor=sun
|
||||
;;
|
||||
-cnk*|-aix*)
|
||||
-aix*)
|
||||
vendor=ibm
|
||||
;;
|
||||
-beos*)
|
||||
|
@ -1,9 +1,10 @@
|
||||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2013-05-30.07; # UTC
|
||||
scriptversion=2006-10-15.18
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 Free Software
|
||||
# Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -16,7 +17,9 @@ scriptversion=2013-05-30.07; # UTC
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
@ -27,9 +30,9 @@ scriptversion=2013-05-30.07; # UTC
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
echo "$0: No command. Try \`$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
@ -39,11 +42,11 @@ as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
source Source file read by `PROGRAMS ARGS'.
|
||||
object Object file output by `PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
tmpdepfile Temporary file to use when outputing dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
@ -56,66 +59,6 @@ EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
# Get the directory component of the given path, and save it in the
|
||||
# global variables '$dir'. Note that this directory component will
|
||||
# be either empty or ending with a '/' character. This is deliberate.
|
||||
set_dir_from ()
|
||||
{
|
||||
case $1 in
|
||||
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||
*) dir=;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Get the suffix-stripped basename of the given path, and save it the
|
||||
# global variable '$base'.
|
||||
set_base_from ()
|
||||
{
|
||||
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||
}
|
||||
|
||||
# If no dependency file was actually created by the compiler invocation,
|
||||
# we still have to create a dummy depfile, to avoid errors with the
|
||||
# Makefile "include basename.Plo" scheme.
|
||||
make_dummy_depfile ()
|
||||
{
|
||||
echo "#dummy" > "$depfile"
|
||||
}
|
||||
|
||||
# Factor out some common post-processing of the generated depfile.
|
||||
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||
aix_post_process_depfile ()
|
||||
{
|
||||
# If the compiler actually managed to produce a dependency file,
|
||||
# post-process it.
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependency.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# $object: dependency.h
|
||||
# and one to simply output
|
||||
# dependency.h:
|
||||
# which is needed to avoid the deleted-header problem.
|
||||
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||
} > "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
}
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
# Character ranges might be problematic outside the C locale.
|
||||
# These definitions help.
|
||||
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
lower=abcdefghijklmnopqrstuvwxyz
|
||||
digits=0123456789
|
||||
alpha=${upper}${lower}
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
@ -128,9 +71,6 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Avoid interferences from the environment.
|
||||
gccflag= dashmflag=
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
@ -142,32 +82,9 @@ if test "$depmode" = hp; then
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
@ -190,7 +107,8 @@ gcc3)
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
@ -198,17 +116,13 @@ gcc3)
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||
## (see the conditional assignment to $gccflag above).
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||
## supported by the other compilers which use the 'gcc' depmode.
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
@ -216,31 +130,31 @@ gcc)
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The second -e expression handles DOS-style file names with drive
|
||||
# letters.
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## This next piece of magic avoids the `deleted header file' problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the `:'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## well.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
@ -258,7 +172,8 @@ sgi)
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
@ -266,156 +181,99 @@ sgi)
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# the IRIX cc adds comments like `#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||
| tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr '
|
||||
' ' ' >> $depfile
|
||||
echo >> $depfile
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> $depfile
|
||||
else
|
||||
make_dummy_depfile
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# current directory. Also, the AIX compiler puts `$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
|
||||
tmpdepfile="$stripped.u"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
|
||||
if test -f "$tmpdepfile"; then :
|
||||
else
|
||||
stripped=`echo "$stripped" | sed 's,^.*/,,'`
|
||||
tmpdepfile="$stripped.u"
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
tcc)
|
||||
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||
# FIXME: That version still under development at the moment of writing.
|
||||
# Make that this statement remains true also for stable, released
|
||||
# versions.
|
||||
# It will wrap lines (doesn't matter whether long or short) with a
|
||||
# trailing '\', as in:
|
||||
#
|
||||
# foo.o : \
|
||||
# foo.c \
|
||||
# foo.h \
|
||||
#
|
||||
# It will put a trailing '\' even on the last line, and will use leading
|
||||
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||
# "Emit spaces for -MD").
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||
# We have to change lines of the first kind to '$object: \'.
|
||||
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||
# dummy dependency, to avoid the deleted-header problem.
|
||||
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then
|
||||
outname="$stripped.o"
|
||||
# Each line is of the form `foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
## The order of this option in the case statement is important, since the
|
||||
## shell code in configure will try each of these formats in the order
|
||||
## listed in this file. A plain '-MD' option would be understood by many
|
||||
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||
pgcc)
|
||||
# Portland's C compiler understands '-MD'.
|
||||
# Will always output deps to 'file.d' where file is the root name of the
|
||||
# source file under compilation, even if file resides in a subdirectory.
|
||||
# The object file name does not affect the name of the '.d' file.
|
||||
# pgcc 10.2 will output
|
||||
icc)
|
||||
# Intel's C compiler understands `-MD -MF file'. However on
|
||||
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want:
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using '\' :
|
||||
# and will wrap long lines using \ :
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
set_dir_from "$object"
|
||||
# Use the source, not the object, to determine the base name, since
|
||||
# that's sadly what pgcc will do too.
|
||||
set_base_from "$source"
|
||||
tmpdepfile=$base.d
|
||||
|
||||
# For projects that build the same source file twice into different object
|
||||
# files, the pgcc approach of using the *source* file root name can cause
|
||||
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||
# the same $tmpdepfile.
|
||||
lockdir=$base.d-lock
|
||||
trap "
|
||||
echo '$0: caught signal, cleaning up...' >&2
|
||||
rmdir '$lockdir'
|
||||
exit 1
|
||||
" 1 2 13 15
|
||||
numtries=100
|
||||
i=$numtries
|
||||
while test $i -gt 0; do
|
||||
# mkdir is a portable test-and-set.
|
||||
if mkdir "$lockdir" 2>/dev/null; then
|
||||
# This process acquired the lock.
|
||||
"$@" -MD
|
||||
stat=$?
|
||||
# Release the lock.
|
||||
rmdir "$lockdir"
|
||||
break
|
||||
else
|
||||
# If the lock is being held by a different process, wait
|
||||
# until the winning process is done or we timeout.
|
||||
while test -d "$lockdir" && test $i -gt 0; do
|
||||
sleep 1
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
fi
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
trap - 1 2 13 15
|
||||
if test $i -le 0; then
|
||||
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||
echo "$0: check lockdir '$lockdir'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test $stat -ne 0; then
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
@ -427,8 +285,8 @@ pgcc)
|
||||
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
|
||||
sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
@ -439,8 +297,9 @@ hp2)
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
@ -451,7 +310,8 @@ hp2)
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
@ -461,107 +321,72 @@ hp2)
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add `dependent.h:' lines.
|
||||
sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in `foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mechanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
# Same post-processing that is required for AIX mode.
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
# That's a tab and a space in the [].
|
||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
@ -574,13 +399,13 @@ dashmstdout)
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
while test $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
# Remove `-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
@ -600,18 +425,18 @@ dashmstdout)
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for ':'
|
||||
# Require at least two characters before searching for `:'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
# a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||
sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
@ -625,51 +450,41 @@ makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
while test $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
cleared=no
|
||||
for arg in "$@"; do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
obj_suffix="`echo $object | sed 's/^.*\././'`"
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed '1,2d' "$tmpdepfile" \
|
||||
| tr ' ' "$nl" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' '
|
||||
' | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
@ -680,13 +495,13 @@ cpp)
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
while test $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
# Remove `-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
@ -705,10 +520,10 @@ cpp)
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E \
|
||||
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||
"$@" -E |
|
||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
@ -718,56 +533,35 @@ cpp)
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
# always write the preprocessed file to stdout, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
"$@" -E |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
|
||||
echo " " >> "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
@ -786,6 +580,5 @@ exit 0
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2013-12-25.23; # UTC
|
||||
scriptversion=2006-10-14.15
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
@ -35,57 +35,57 @@ scriptversion=2013-12-25.23; # UTC
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
IFS=" "" $nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
posix_glob=
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
chgrpcmd=
|
||||
stripcmd=
|
||||
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
dstarg=
|
||||
no_target_directory=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
@ -95,101 +95,91 @@ In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
-c (ignored)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
-c) shift
|
||||
continue;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
shift
|
||||
shift
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
-s) stripcmd=$stripprog
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
-t) dstarg=$2
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
-T) no_target_directory=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
if test -n "$dstarg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
set fnord "$@" "$dstarg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
dstarg=$arg
|
||||
done
|
||||
fi
|
||||
|
||||
@ -198,26 +188,13 @@ if test $# -eq 0; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
trap '(exit $?); exit' 1 2 13 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
@ -228,16 +205,16 @@ if test -z "$dir_arg"; then
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
@ -245,9 +222,9 @@ fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
# Protect names starting with `-'.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
-*) src=./$src ;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
@ -265,24 +242,55 @@ do
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
if test -z "$dstarg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
dst=$dstarg
|
||||
# Protect names starting with `-'.
|
||||
case $dst in
|
||||
-*) dst=./$dst ;;
|
||||
esac
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dstarg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
@ -293,74 +301,74 @@ do
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
@ -370,51 +378,60 @@ do
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
/*) prefix=/ ;;
|
||||
-*) prefix=./ ;;
|
||||
*) prefix= ;;
|
||||
esac
|
||||
|
||||
case $posix_glob in
|
||||
'')
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=true
|
||||
else
|
||||
posix_glob=false
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
$posix_glob && set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
$posix_glob && set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
test -z "$d" && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@ -442,51 +459,41 @@ do
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
|
||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
|
||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
|
||||
&& { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
# Now rename the file to the real destination.
|
||||
{ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \
|
||||
|| {
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
if test -f "$dst"; then
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null \
|
||||
|| { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
|
||||
&& { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
|
||||
|| {
|
||||
echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
else
|
||||
:
|
||||
fi
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
} || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
@ -496,6 +503,5 @@ done
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
|
@ -1,10 +1,11 @@
|
||||
#! /bin/sh
|
||||
# Common wrapper for a few potentially missing GNU programs.
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
|
||||
scriptversion=2013-10-28.13; # UTC
|
||||
scriptversion=2006-05-10.23
|
||||
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
|
||||
# Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -17,7 +18,9 @@ scriptversion=2013-10-28.13; # UTC
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
@ -25,40 +28,66 @@ scriptversion=2013-10-28.13; # UTC
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
|
||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case $1 in
|
||||
|
||||
--is-lightweight)
|
||||
# Used by our autoconf macros to check whether the available missing
|
||||
# script is modern enough.
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--run)
|
||||
# Back-compat with the calling convention used by older automake.
|
||||
shift
|
||||
;;
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||
to PROGRAM being missing or too old.
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal autoconf autoheader autom4te automake makeinfo
|
||||
bison yacc flex lex help2man
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||
'g' are ignored when checking the name.
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
autom4te touch the output file, or create a stub one
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
tar try tar, gnutar, gtar, then tar without non-portable flags
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
@ -70,146 +99,269 @@ Send bug reports to <bug-automake@gnu.org>."
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: unknown '$1' option"
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Run the given program, remember its exit status.
|
||||
"$@"; st=$?
|
||||
|
||||
# If it succeeded, we are done.
|
||||
test $st -eq 0 && exit 0
|
||||
|
||||
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||
# passed; such an option is passed most likely to detect whether the
|
||||
# program is present and works.
|
||||
case $2 in --version|--help) exit $st;; esac
|
||||
|
||||
# Exit code 63 means version mismatch. This often happens when the user
|
||||
# tries to use an ancient version of a tool on a file that requires a
|
||||
# minimum version.
|
||||
if test $st -eq 63; then
|
||||
msg="probably too old"
|
||||
elif test $st -eq 127; then
|
||||
# Program was missing.
|
||||
msg="missing on your system"
|
||||
else
|
||||
# Program was found and executed, but failed. Give up.
|
||||
exit $st
|
||||
fi
|
||||
|
||||
perl_URL=http://www.perl.org/
|
||||
flex_URL=http://flex.sourceforge.net/
|
||||
gnu_software_URL=http://www.gnu.org/software
|
||||
|
||||
program_details ()
|
||||
{
|
||||
case $1 in
|
||||
aclocal|automake)
|
||||
echo "The '$1' program is part of the GNU Automake package:"
|
||||
echo "<$gnu_software_URL/automake>"
|
||||
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/autoconf>"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
autoconf|autom4te|autoheader)
|
||||
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||
echo "<$gnu_software_URL/autoconf/>"
|
||||
echo "It also requires GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice ()
|
||||
{
|
||||
# Normalize program name to check for.
|
||||
normalized_program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
printf '%s\n' "'$1' is $msg."
|
||||
|
||||
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||
case $normalized_program in
|
||||
autoconf*)
|
||||
echo "You should only need it if you modified 'configure.ac',"
|
||||
echo "or m4 files included by it."
|
||||
program_details 'autoconf'
|
||||
;;
|
||||
autoheader*)
|
||||
echo "You should only need it if you modified 'acconfig.h' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'autoheader'
|
||||
;;
|
||||
automake*)
|
||||
echo "You should only need it if you modified 'Makefile.am' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'automake'
|
||||
;;
|
||||
aclocal*)
|
||||
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'aclocal'
|
||||
;;
|
||||
autom4te*)
|
||||
echo "You might have modified some maintainer files that require"
|
||||
echo "the 'autom4te' program to be rebuilt."
|
||||
program_details 'autom4te'
|
||||
;;
|
||||
bison*|yacc*)
|
||||
echo "You should only need it if you modified a '.y' file."
|
||||
echo "You may want to install the GNU Bison package:"
|
||||
echo "<$gnu_software_URL/bison/>"
|
||||
;;
|
||||
lex*|flex*)
|
||||
echo "You should only need it if you modified a '.l' file."
|
||||
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||
echo "<$flex_URL>"
|
||||
;;
|
||||
help2man*)
|
||||
echo "You should only need it if you modified a dependency" \
|
||||
"of a man page."
|
||||
echo "You may want to install the GNU Help2man package:"
|
||||
echo "<$gnu_software_URL/help2man/>"
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program).
|
||||
case $1 in
|
||||
lex|yacc)
|
||||
# Not GNU programs, they don't have --version.
|
||||
;;
|
||||
makeinfo*)
|
||||
echo "You should only need it if you modified a '.texi' file, or"
|
||||
echo "any other file indirectly affecting the aspect of the manual."
|
||||
echo "You might want to install the Texinfo package:"
|
||||
echo "<$gnu_software_URL/texinfo/>"
|
||||
echo "The spurious makeinfo call might also be the consequence of"
|
||||
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||
echo "want to install GNU make:"
|
||||
echo "<$gnu_software_URL/make/>"
|
||||
;;
|
||||
*)
|
||||
echo "You might have modified some files without having the proper"
|
||||
echo "tools for further handling them. Check the 'README' file, it"
|
||||
echo "often tells you about the needed prerequisites for installing"
|
||||
echo "this package. You may also peek at any GNU archive site, in"
|
||||
echo "case some other package contains this missing '$1' program."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||
-e '2,$s/^/ /' >&2
|
||||
tar)
|
||||
if test -n "$run"; then
|
||||
echo 1>&2 "ERROR: \`tar' requires --run"
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
# Propagate the correct exit status (expected to be 127 for a program
|
||||
# not found, 63 for a program that failed due to version mismatch).
|
||||
exit $st
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case $1 in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case $f in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison|yacc)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG="\${$#}"
|
||||
case $LASTARG in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f y.tab.h; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if test ! -f y.tab.c; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex|flex)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG="\${$#}"
|
||||
case $LASTARG in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f lex.yy.c; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '
|
||||
/^@setfilename/{
|
||||
s/.* \([^ ]*\) *$/\1/
|
||||
p
|
||||
q
|
||||
}' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
tar)
|
||||
shift
|
||||
|
||||
# We have already tried tar in the generic part.
|
||||
# Look for gnutar/gtar before invocation to avoid ugly error
|
||||
# messages.
|
||||
if (gnutar --version > /dev/null 2>&1); then
|
||||
gnutar "$@" && exit 0
|
||||
fi
|
||||
if (gtar --version > /dev/null 2>&1); then
|
||||
gtar "$@" && exit 0
|
||||
fi
|
||||
firstarg="$1"
|
||||
if shift; then
|
||||
case $firstarg in
|
||||
*o*)
|
||||
firstarg=`echo "$firstarg" | sed s/o//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
case $firstarg in
|
||||
*h*)
|
||||
firstarg=`echo "$firstarg" | sed s/h//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: I can't seem to be able to run \`tar' with the given arguments.
|
||||
You may want to install GNU tar or Free paxutils, or check the
|
||||
command line arguments."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
|
@ -1,148 +0,0 @@
|
||||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
scriptversion=2013-07-13.22; # UTC
|
||||
|
||||
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--]
|
||||
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
missing_opts=
|
||||
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||||
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||||
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||||
if test x"$missing_opts" != x; then
|
||||
usage_error "the following mandatory options are missing:$missing_opts"
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
usage_error "missing argument"
|
||||
fi
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
"$@" >$log_file 2>&1
|
||||
estatus=$?
|
||||
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
tweaked_estatus=1
|
||||
else
|
||||
tweaked_estatus=$estatus
|
||||
fi
|
||||
|
||||
case $tweaked_estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report the test outcome and exit status in the logs, so that one can
|
||||
# know whether the test passed or failed simply by looking at the '.log'
|
||||
# file, without the need of also peaking into the corresponding '.trs'
|
||||
# file (automake bug#11814).
|
||||
echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/@PACKAGE_NAME@
|
||||
|
||||
Name: google-breakpad-client
|
||||
Description: An open-source multi-platform crash reporting system
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lbreakpad_client @PTHREAD_LIBS@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@
|
@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/@PACKAGE_NAME@
|
||||
|
||||
Name: google-breakpad
|
||||
Description: An open-source multi-platform crash reporting system
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lbreakpad @PTHREAD_LIBS@
|
||||
Cflags: -I${includedir} @PTHREAD_CFLAGS@
|
@ -1,5 +1,5 @@
|
||||
# This file is used by gcl to get repository specific information.
|
||||
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||
CODE_REVIEW_SERVER: breakpad.appspot.com
|
||||
CC_LIST: google-breakpad-dev@googlegroups.com
|
||||
TRY_ON_UPLOAD: False
|
||||
VIEW_VC: https://chromium.googlesource.com/breakpad/breakpad/+/
|
||||
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r=
|
||||
|
93
configure.ac
93
configure.ac
@ -32,25 +32,33 @@ AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
AC_CONFIG_SRCDIR(README.md)
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(autotools)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
||||
AM_CONFIG_HEADER(src/config.h)
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
AM_PROG_AR
|
||||
AM_PROG_AS
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
||||
|
||||
dnl This must come before all the feature tests below.
|
||||
AC_HEADER_STDC
|
||||
m4_include(m4/ax_pthread.m4)
|
||||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h])
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
LINUX_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue)
|
||||
|
||||
AC_ARG_ENABLE(m32,
|
||||
AS_HELP_STRING([--enable-m32],
|
||||
[Compile/build with -m32]
|
||||
@ -70,39 +78,6 @@ AC_ARG_ENABLE(m32,
|
||||
esac],
|
||||
[usem32=false])
|
||||
|
||||
AC_HEADER_STDC
|
||||
AC_SYS_LARGEFILE
|
||||
m4_include(m4/ax_pthread.m4)
|
||||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h])
|
||||
|
||||
m4_include(m4/ax_cxx_compile_stdcxx.m4)
|
||||
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
LINUX_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue)
|
||||
|
||||
# Only use Android support headers when compiling for Android
|
||||
case $host in
|
||||
*-android*)
|
||||
ANDROID_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue)
|
||||
|
||||
# Some tools (like mac ones) only support x86 currently.
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
X86_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(X86_HOST, test x$X86_HOST = xtrue)
|
||||
|
||||
AC_ARG_ENABLE(processor,
|
||||
AS_HELP_STRING([--disable-processor],
|
||||
[Don't build processor library]
|
||||
@ -143,39 +118,6 @@ if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools
|
||||
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(system-test-libs,
|
||||
AS_HELP_STRING([--enable-system-test-libs],
|
||||
[Use gtest/gmock/etc... from the system instead ]
|
||||
[of the local copies (default is local)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
system_test_libs=true
|
||||
;;
|
||||
no)
|
||||
system_test_libs=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --enable-system-test-libs)
|
||||
;;
|
||||
esac],
|
||||
[system_test_libs=false])
|
||||
AM_CONDITIONAL(SYSTEM_TEST_LIBS, test x$system_test_libs = xtrue)
|
||||
|
||||
AC_ARG_VAR([GMOCK_CONFIG], [Path to gmock-config script])
|
||||
AC_ARG_VAR([GMOCK_CFLAGS], [Compiler flags for gmock])
|
||||
AC_ARG_VAR([GMOCK_LIBS], [Linker flags for gmock])
|
||||
AC_ARG_VAR([GTEST_CONFIG], [Path to gtest-config script])
|
||||
AC_ARG_VAR([GTEST_CFLAGS], [Compiler flags for gtest])
|
||||
AC_ARG_VAR([GTEST_LIBS], [Linker flags for gtest])
|
||||
if test x$system_test_libs = xtrue; then
|
||||
AC_CHECK_TOOL([GMOCK_CONFIG], [gmock-config])
|
||||
AC_CHECK_TOOL([GTEST_CONFIG], [gtest-config])
|
||||
GMOCK_CFLAGS=`$GMOCK_CONFIG --cppflags --cxxflags`
|
||||
GMOCK_LIBS=`$GMOCK_CONFIG --ldflags --libs`
|
||||
GTEST_CFLAGS=`$GTEST_CONFIG --cppflags --cxxflags`
|
||||
GTEST_LIBS=`$GTEST_CONFIG --ldflags --libs`
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(selftest,
|
||||
AS_HELP_STRING([--enable-selftest],
|
||||
[Run extra tests with "make check" ]
|
||||
@ -195,10 +137,5 @@ AC_ARG_ENABLE(selftest,
|
||||
[selftest=false])
|
||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
||||
|
||||
AC_CONFIG_FILES(m4_flatten([
|
||||
breakpad.pc
|
||||
breakpad-client.pc
|
||||
Makefile
|
||||
]))
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
@ -1 +0,0 @@
|
||||
*
|
Binary file not shown.
Before Width: | Height: | Size: 82 KiB |
1023
docs/breakpad.svg
1023
docs/breakpad.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 45 KiB |
@ -1,224 +0,0 @@
|
||||
# Breakpad Client Libraries
|
||||
|
||||
## Objective
|
||||
|
||||
The Breakpad client libraries are responsible for monitoring an application for
|
||||
crashes (exceptions), handling them when they occur by generating a dump, and
|
||||
providing a means to upload dumps to a crash reporting server. These tasks are
|
||||
divided between the “handler” (short for “exception handler”) library linked in
|
||||
to an application being monitored for crashes, and the “sender” library,
|
||||
intended to be linked in to a separate external program.
|
||||
|
||||
## Background
|
||||
|
||||
As one of the chief tasks of the client handler is to generate a dump, an
|
||||
understanding of [dump files](processor_design.md) will aid in understanding the
|
||||
handler.
|
||||
|
||||
## Overview
|
||||
|
||||
Breakpad provides client libraries for each of its target platforms. Currently,
|
||||
these exist for Windows on x86 and Mac OS X on both x86 and PowerPC. A Linux
|
||||
implementation has been written and is currently under review.
|
||||
|
||||
Because the mechanisms for catching exceptions and the methods for obtaining the
|
||||
information that a dump contains vary between operating systems, each target
|
||||
operating system requires a completely different handler implementation. Where
|
||||
multiple CPUs are supported for a single operating system, the handler
|
||||
implementation will likely also require separate code for each processor type to
|
||||
extract CPU-specific information. One of the goals of the Breakpad handler is to
|
||||
provide a prepackaged cross-platform system that masks many of these
|
||||
system-level differences and quirks from the application developer. Although the
|
||||
underlying implementations differ, the handler library for each system follows
|
||||
the same set of principles and exposes a similar interface.
|
||||
|
||||
Code that wishes to take advantage of Breakpad should be linked against the
|
||||
handler library, and should, at an appropriate time, install a Breakpad handler.
|
||||
For applications, it is generally desirable to install the handler as early in
|
||||
the start-up process as possible. Developers of library code using Breakpad to
|
||||
monitor itself may wish to install a Breakpad handler when the library is
|
||||
loaded, or may only want to install a handler when calls are made in to the
|
||||
library.
|
||||
|
||||
The handler can be triggered to generate a dump either by catching an exception
|
||||
or at the request of the application itself. The latter case may be useful in
|
||||
debugging assertions or other conditions where developers want to know how a
|
||||
program got in to a specific non-crash state. After generating a dump, the
|
||||
handler calls a user-specified callback function. The callback function may
|
||||
collect additional data about the program’s state, quit the program, launch a
|
||||
crash reporter application, or perform other tasks. Allowing for this
|
||||
functionality to be dictated by a callback function preserves flexibility.
|
||||
|
||||
The sender library is also has a separate implementation for each supported
|
||||
platform, because of the varying interfaces for accessing network resources on
|
||||
different operating systems. The sender transmits a dump along with other
|
||||
application-defined information to a crash report server via HTTP. Because dumps
|
||||
may contain sensitive data, the sender allows for the use of HTTPS.
|
||||
|
||||
The canonical example of the entire client system would be for a monitored
|
||||
application to link against the handler library, install a Breakpad handler from
|
||||
its main function, and provide a callback to launch a small crash reporter
|
||||
program. The crash reporter program would be linked against the sender library,
|
||||
and would send the crash dump when launched. A separate process is recommended
|
||||
for this function because of the unreliability inherent in doing any significant
|
||||
amount of work from a crashed process.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Exception Handler Installation
|
||||
|
||||
The mechanisms for installing an exception handler vary between operating
|
||||
systems. On Windows, it’s a relatively simple matter of making one call to
|
||||
register a [top-level exception filter]
|
||||
(http://msdn.microsoft.com/library/en-us/debug/base/setunhandledexceptionfilter.asp)
|
||||
callback function. On most Unix-like systems such as Linux, processes are
|
||||
informed of exceptions by the delivery of a signal, so an exception handler
|
||||
takes the form of a signal handler. The native mechanism to catch exceptions on
|
||||
Mac OS X requires a large amount of code to set up a Mach port, identify it as
|
||||
the exception port, and assign a thread to listen for an exception on that port.
|
||||
Just as the preparation of exception handlers differ, the manner in which they
|
||||
are called differs as well. On Windows and most Unix-like systems, the handler
|
||||
is called on the thread that caused the exception. On Mac OS X, the thread
|
||||
listening to the exception port is notified that an exception has occurred. The
|
||||
different implementations of the Breakpad handler libraries perform these tasks
|
||||
in the appropriate ways on each platform, while exposing a similar interface on
|
||||
each.
|
||||
|
||||
A Breakpad handler is embodied in an `ExceptionHandler` object. Because it’s a
|
||||
C++ object, `ExceptionHandler`s may be created as local variables, allowing them
|
||||
to be installed and removed as functions are called and return. This provides
|
||||
one possible way for a developer to monitor only a portion of an application for
|
||||
crashes.
|
||||
|
||||
### Exception Basics
|
||||
|
||||
Once an application encounters an exception, it is in an indeterminate and
|
||||
possibly hazardous state. Consequently, any code that runs after an exception
|
||||
occurs must take extreme care to avoid performing operations that might fail,
|
||||
hang, or cause additional exceptions. This task is not at all straightforward,
|
||||
and the Breakpad handler library seeks to do it properly, accounting for all of
|
||||
the minute details while allowing other application developers, even those with
|
||||
little systems programming experience, to reap the benefits. All of the Breakpad
|
||||
handler code that executes after an exception occurs has been written according
|
||||
to the following guidelines for safety at exception time:
|
||||
|
||||
* Use of the application heap is forbidden. The heap may be corrupt or
|
||||
otherwise unusable, and allocators may not function.
|
||||
* Resource allocation must be severely limited. The handler may create a new
|
||||
file to contain the dump, and it may attempt to launch a process to continue
|
||||
handling the crash.
|
||||
* Execution on the thread that caused the exception is significantly limited.
|
||||
The only code permitted to execute on this thread is the code necessary to
|
||||
transition handling to a dedicated preallocated handler thread, and the code
|
||||
to return from the exception handler.
|
||||
* Handlers shouldn’t handle crashes by attempting to walk stacks themselves,
|
||||
as stacks may be in inconsistent states. Dump generation should be performed
|
||||
by interfacing with the operating system’s memory manager and code module
|
||||
manager.
|
||||
* Library code, including runtime library code, must be avoided unless it
|
||||
provably meets the above guidelines. For example, this means that the STL
|
||||
string class may not be used, because it performs operations that attempt to
|
||||
allocate and use heap memory. It also means that many C runtime functions
|
||||
must be avoided, particularly on Windows, because of heap operations that
|
||||
they may perform.
|
||||
|
||||
A dedicated handler thread is used to preserve the state of the exception thread
|
||||
when an exception occurs: during dump generation, it is difficult if not
|
||||
impossible for a thread to accurately capture its own state. Performing all
|
||||
exception-handling functions on a separate thread is also critical when handling
|
||||
stack-limit-exceeded exceptions. It would be hazardous to run out of stack space
|
||||
while attempting to handle an exception. Because of the rule against allocating
|
||||
resources at exception time, the Breakpad handler library creates its handler
|
||||
thread when it installs its exception handler. On Mac OS X, this handler thread
|
||||
is created during the normal setup of the exception handler, and the handler
|
||||
thread will be signaled directly in the event of an exception. On Windows and
|
||||
Linux, the handler thread is signaled by a small amount of code that executes on
|
||||
the exception thread. Because the code that executes on the exception thread in
|
||||
this case is small and safe, this does not pose a problem. Even when an
|
||||
exception is caused by exceeding stack size limits, this code is sufficiently
|
||||
compact to execute entirely within the stack’s guard page without causing an
|
||||
exception.
|
||||
|
||||
The handler thread may also be triggered directly by a user call, even when no
|
||||
exception occurs, to allow dumps to be generated at any point deemed
|
||||
interesting.
|
||||
|
||||
### Filter Callback
|
||||
|
||||
When the handler thread begins handling an exception, it calls an optional
|
||||
user-defined filter callback function, which is responsible for judging whether
|
||||
Breakpad’s handler should continue handling the exception or not. This mechanism
|
||||
is provided for the benefit of library or plug-in code, whose developers may not
|
||||
be interested in reports of crashes that occur outside of their modules but
|
||||
within processes hosting their code. If the filter callback indicates that it is
|
||||
not interested in the exception, the Breakpad handler arranges for it to be
|
||||
delivered to any previously-installed handler.
|
||||
|
||||
### Dump Generation
|
||||
|
||||
Assuming that the filter callback approves (or does not exist), the handler
|
||||
writes a dump in a directory specified by the application developer when the
|
||||
handler was installed, using a previously generated unique identifier to avoid
|
||||
name collisions. The mechanics of dump generation also vary between platforms,
|
||||
but in general, the process involves enumerating each thread of execution, and
|
||||
capturing its state, including processor context and the active portion of its
|
||||
stack area. The dump also includes a list of the code modules loaded in to the
|
||||
application, and an indicator of which thread generated the exception or
|
||||
requested the dump. In order to avoid allocating memory during this process, the
|
||||
dump is written in place on disk.
|
||||
|
||||
### Post-Dump Behavior
|
||||
|
||||
Upon completion of writing the dump, a second callback function is called. This
|
||||
callback may be used to launch a separate crash reporting program or to collect
|
||||
additional data from the application. The callback may also be used to influence
|
||||
whether Breakpad will treat the exception as handled or unhandled. Even after a
|
||||
dump is successfully generated, Breakpad can be made to behave as though it
|
||||
didn’t actually handle an exception. This function may be useful for developers
|
||||
who want to test their applications with Breakpad enabled but still retain the
|
||||
ability to use traditional debugging techniques. It also allows a
|
||||
Breakpad-enabled application to coexist with a platform’s native crash reporting
|
||||
system, such as Mac OS X’ [CrashReporter]
|
||||
(http://developer.apple.com/technotes/tn2004/tn2123.html) and [Windows Error
|
||||
Reporting](http://msdn.microsoft.com/isv/resources/wer/).
|
||||
|
||||
Typically, when Breakpad handles an exception fully and no debuggers are
|
||||
involved, the crashed process will terminate.
|
||||
|
||||
Authors of both callback functions that execute within a Breakpad handler are
|
||||
cautioned that their code will be run at exception time, and that as a result,
|
||||
they should observe the same programming practices that the Breakpad handler
|
||||
itself adheres to. Notably, if a callback is to be used to collect additional
|
||||
data from an application, it should take care to read only “safe” data. This
|
||||
might involve accessing only static memory locations that are updated
|
||||
periodically during the course of normal program execution.
|
||||
|
||||
### Sender Library
|
||||
|
||||
The Breakpad sender library provides a single function to send a crash report to
|
||||
a crash server. It accepts a crash server’s URL, a map of key-value parameters
|
||||
that will accompany the dump, and the path to a dump file itself. Each of the
|
||||
key-value parameters and the dump file are sent as distinct parts of a multipart
|
||||
HTTP POST request to the specified URL using the platform’s native HTTP
|
||||
facilities. On Linux, [libcurl](http://curl.haxx.se/) is used for this function,
|
||||
as it is the closest thing to a standard HTTP library available on that
|
||||
platform.
|
||||
|
||||
## Future Plans
|
||||
|
||||
Although we’ve had great success with in-process dump generation by following
|
||||
our guidelines for safe code at exception time, we are exploring options for
|
||||
allowing dumps to be generated in a separate process, to further enhance the
|
||||
handler library’s robustness.
|
||||
|
||||
On Windows, we intend to offer tools to make it easier for Breakpad’s settings
|
||||
to be managed by the native group policy management system.
|
||||
|
||||
We also plan to offer tools that many developers would find desirable in the
|
||||
context of handling crashes, such as a mechanism to determine at launch if the
|
||||
program last terminated in a crash, and a way to calculate “crashiness” in terms
|
||||
of crashes over time or the number of application launches between crashes.
|
||||
|
||||
We are also investigating methods to capture crashes that occur early in an
|
||||
application’s launch sequence, including crashes that occur before a program’s
|
||||
main function begins executing.
|
@ -1,35 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
Thanks for thinking of contributing to Breakpad! Unfortunately there are some
|
||||
pesky legal issues to get out of the way, but they're quick and painless.
|
||||
|
||||
## Legal
|
||||
|
||||
If you're doing work individually, not as part of any employment, you'll need to
|
||||
sign the <a
|
||||
href='http://code.google.com/legal/individual-cla-v1.0.html'>Individual
|
||||
Contributor License Agreement</a>. This agreement can be completed
|
||||
electronically.
|
||||
|
||||
If you're contributing to Breakpad as part of your employment with another
|
||||
organization, you'll need to sign a <a
|
||||
href='http://code.google.com/legal/corporate-cla-v1.0.html'> Corporate
|
||||
Contributor License Agreement</a>. Once completed this document will need to be
|
||||
faxed.
|
||||
|
||||
**_IMPORTANT_**: The authors(you!) of the contributions will maintain all
|
||||
copyrights; the agreements you sign will grant rights to Google to use your
|
||||
work.
|
||||
|
||||
Thanks, and if you have any questions let me know and I'll loop in the legal guy
|
||||
here to get you an answer.
|
||||
|
||||
## Technical
|
||||
|
||||
Once you have signed the agreement you can be added to our contributors list and
|
||||
have write access to code. For full details on getting started see our trunk
|
||||
`README`.
|
||||
|
||||
## List of people who have signed contributor agreements
|
||||
|
||||
None so far.
|
@ -1,128 +0,0 @@
|
||||
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.
|
@ -1,121 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
Breakpad is a library and tool suite that allows you to distribute an
|
||||
application to users with compiler-provided debugging information removed,
|
||||
record crashes in compact "minidump" files, send them back to your server, and
|
||||
produce C and C++ stack traces from these minidumps. Breakpad can also write
|
||||
minidumps on request for programs that have not crashed.
|
||||
|
||||
Breakpad is currently used by Google Chrome, Firefox, Google Picasa, Camino,
|
||||
Google Earth, and other projects.
|
||||
|
||||
![http://google-breakpad.googlecode.com/svn/wiki/breakpad.png]
|
||||
(http://google-breakpad.googlecode.com/svn/wiki/breakpad.png)
|
||||
|
||||
Breakpad has three main components:
|
||||
|
||||
* The **client** is a library that you include in your application. It can
|
||||
write minidump files capturing the current threads' state and the identities
|
||||
of the currently loaded executable and shared libraries. You can configure
|
||||
the client to write a minidump when a crash occurs, or when explicitly
|
||||
requested.
|
||||
|
||||
* The **symbol dumper** is a program that reads the debugging information
|
||||
produced by the compiler and produces a **symbol file**, in [Breakpad's own
|
||||
format](symbol_files.md).
|
||||
|
||||
* The **processor** is a program that reads a minidump file, finds the
|
||||
appropriate symbol files for the versions of the executables and shared
|
||||
libraries the minidump mentions, and produces a human-readable C/C++ stack
|
||||
trace.
|
||||
|
||||
# The minidump file format
|
||||
|
||||
The minidump file format is similar to core files but was developed by Microsoft
|
||||
for its crash-uploading facility. A minidump file contains:
|
||||
|
||||
* A list of the executable and shared libraries that were loaded in the
|
||||
process at the time the dump was created. This list includes both file names
|
||||
and identifiers for the particular versions of those files that were loaded.
|
||||
|
||||
* A list of threads present in the process. For each thread, the minidump
|
||||
includes the state of the processor registers, and the contents of the
|
||||
threads' stack memory. These data are uninterpreted byte streams, as the
|
||||
Breakpad client generally has no debugging information available to produce
|
||||
function names or line numbers, or even identify stack frame boundaries.
|
||||
|
||||
* Other information about the system on which the dump was collected:
|
||||
processor and operating system versions, the reason for the dump, and so on.
|
||||
|
||||
Breakpad uses Windows minidump files on all platforms, instead of the
|
||||
traditional core files, for several reasons:
|
||||
|
||||
* Core files can be very large, making them impractical to send across a
|
||||
network to the collector for processing. Minidumps are smaller, as they were
|
||||
designed to be used this way.
|
||||
|
||||
* The core file format is poorly documented. For example, the Linux Standards
|
||||
Base does not describe how registers are stored in `PT_NOTE` segments.
|
||||
|
||||
* It is harder to persuade a Windows machine to produce a core dump file than
|
||||
it is to persuade other machines to write a minidump file.
|
||||
|
||||
* It simplifies the Breakpad processor to support only one file format.
|
||||
|
||||
# Overview/Life of a minidump
|
||||
|
||||
A minidump is generated via calls into the Breakpad library. By default,
|
||||
initializing Breakpad installs an exception/signal handler that writes a
|
||||
minidump to disk at exception time. On Windows, this is done via
|
||||
`SetUnhandledExceptionFilter()`; on OS X, this is done by creating a thread that
|
||||
waits on the Mach exception port; and on Linux, this is done by installing a
|
||||
signal handler for various exceptions like `SIGILL, SIGSEGV` etc.
|
||||
|
||||
Once the minidump is generated, each platform has a slightly different way of
|
||||
uploading the crash dump. On Windows & Linux, a separate library of functions is
|
||||
provided that can be called into to do the upload. On OS X, a separate process
|
||||
is spawned that prompts the user for permission, if configured to do so, and
|
||||
sends the file.
|
||||
|
||||
# Terminology
|
||||
|
||||
**In-process vs. out-of-process exception handling** - it's generally considered
|
||||
that writing the minidump from within the crashed process is unsafe - key
|
||||
process data structures could be corrupted, or the stack on which the exception
|
||||
handler runs could have been overwritten, etc. All 3 platforms support what's
|
||||
known as "out-of-process" exception handling.
|
||||
|
||||
# Integration overview
|
||||
|
||||
## Breakpad Code Overview
|
||||
|
||||
All the client-side code is found by visiting the Google Project at
|
||||
http://code.google.com/p/google-breakpad. The following directory structure is
|
||||
present in the `src` directory:
|
||||
|
||||
* `processor` Contains minidump-processing code that is used on the server
|
||||
side and isn't of use on the client side
|
||||
* `client` Contains client minidump-generation libraries for all platforms
|
||||
* `tools` Contains source code & projects for building various tools on each
|
||||
platform.
|
||||
|
||||
(Among other directories)
|
||||
|
||||
* <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/WindowsClientIntegration'>Windows
|
||||
Integration Guide</a>
|
||||
* <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/MacBreakpadStarterGuide'>Mac
|
||||
Integration Guide</a>
|
||||
* <a href='http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide'>
|
||||
Linux Integration Guide</a>
|
||||
|
||||
## Build process specifics(symbol generation)
|
||||
|
||||
This applies to all platforms. Inside `src/tools/{platform}/dump_syms` is a tool
|
||||
that can read debugging information for each platform (e.g. for OS X/Linux,
|
||||
DWARF and STABS, and for Windows, PDB files) and generate a Breakpad symbol
|
||||
file. This tool should be run on your binary before it's stripped(in the case of
|
||||
OS X/Linux) and the symbol files need to be stored somewhere that the minidump
|
||||
processor can find. There is another tool, `symupload`, that can be used to
|
||||
upload symbol files if you have written a server that can accept them.
|
@ -1,110 +0,0 @@
|
||||
# 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][1].
|
||||
|
||||
[1]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/client/linux/handler/exception_handler.h
|
||||
|
||||
**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][2], to avoid calling directly into
|
||||
libc, as well as [a header file for making Linux system calls][3] (in **src/third\_party/lss**) to avoid calling into other shared libraries.
|
||||
|
||||
[2]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/linux/linux_libc_support.h
|
||||
[3]: https://chromium.googlesource.com/linux-syscall-support/+/master
|
||||
|
||||
## 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][4] that you might find useful, as well as [a minidump upload tool][5].
|
||||
|
||||
[4]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/linux/http_upload.h
|
||||
[5]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/tools/linux/symupload/minidump_upload.cc
|
||||
|
||||
## 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][6]. 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`:
|
||||
|
||||
[6]: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
|
||||
|
||||
```
|
||||
$ google-breakpad/src/tools/linux/dump_syms/dump_syms ./test > test.sym
|
||||
```
|
||||
|
||||
In order to use these symbols with the `minidump_stackwalk` tool, you will need
|
||||
to place them in a specific directory structure. The first line of the symbol
|
||||
file contains the information you need to produce this directory structure, for
|
||||
example (your output will vary):
|
||||
|
||||
```
|
||||
$ head -n1 test.sym MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test
|
||||
$ mkdir -p ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
|
||||
$ mv test.sym ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
|
||||
```
|
||||
|
||||
You may also find the [symbolstore.py][7] script in the Mozilla repository useful, as it encapsulates these steps.
|
||||
|
||||
[7]: https://dxr.mozilla.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py
|
||||
|
||||
## Processing the minidump to produce a stack trace
|
||||
|
||||
Breakpad includes a tool called `minidump_stackwalk` which can take a minidump
|
||||
plus its corresponding text-format symbols and produce a symbolized stacktrace.
|
||||
It should be in the **google-breakpad/src/processor** directory if you compiled
|
||||
the Breakpad source using the directions above. Simply pass it the minidump and
|
||||
the symbol path as commandline parameters:
|
||||
|
||||
```
|
||||
$ google-breakpad/src/processor/minidump_stackwalk minidump.dmp ./symbols
|
||||
```
|
||||
|
||||
It produces verbose output on stderr, and the stacktrace on stdout, so you may
|
||||
want to redirect stderr.
|
@ -1,47 +0,0 @@
|
||||
# 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.
|
@ -1,184 +0,0 @@
|
||||
# How To Add Breakpad To Your Mac Client Application
|
||||
|
||||
This document is a step-by-step recipe to get your Mac client app to build with
|
||||
Breakpad.
|
||||
|
||||
## Preparing a binary build of Breakpad for use in your tree
|
||||
|
||||
You can either check in a binary build of the Breakpad framework & tools or
|
||||
build it as a dependency of your project. The former is recommended, and
|
||||
detailed here, since building dependencies through other projects is
|
||||
problematic(matching up configuration names), and the Breakpad code doesn't
|
||||
change nearly often enough as your application's will.
|
||||
|
||||
## Building the requisite targets
|
||||
|
||||
All directories are relative to the `src` directory of the Breakpad checkout.
|
||||
|
||||
* Build the 'All' target of `client/mac/Breakpad.xcodeproj` in Release mode.
|
||||
* Execute `cp -R client/mac/build/Release/Breakpad.framework <location in your
|
||||
source tree>`
|
||||
* Inside `tools/mac/dump_syms` directory, build dump\_syms.xcodeproj, and copy
|
||||
tools/mac/dump\_syms/build/Release/dump\_syms to a safe location where it
|
||||
can be run during the build process.
|
||||
|
||||
## Adding Breakpad.framework
|
||||
|
||||
Inside your application's framework, add the Breakpad.Framework to your
|
||||
project's framework settings. When you select it from the file chooser, it will
|
||||
let you pick a target to add it to; go ahead and check the one that's relevant
|
||||
to your application.
|
||||
|
||||
## Copy Breakpad into your Application Package
|
||||
|
||||
Copy Breakpad into your Application Package, so it will be around at run time.
|
||||
|
||||
Go to the Targets section of your Xcode Project window. Hit the disclosure
|
||||
triangle to reveal the build phases of your application. Add a new Copy Files
|
||||
phase using the Contextual menu (Control Click). On the General panel of the new
|
||||
'Get Info' of this new phase, set the destination to 'Frameworks' Close the
|
||||
'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks'
|
||||
Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever
|
||||
it appears in the project file tree.
|
||||
|
||||
## Add a New Run Script build phase
|
||||
|
||||
Near the end of the build phases, add a new Run Script build phase. This will be
|
||||
run before Xcode calls /usr/bin/strip on your project. This is where you'll be
|
||||
calling dump\_sym to output the symbols for each architecture of your build. In
|
||||
my case, the relevant lines read:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
$TOOL_DIR=<location of dump_syms from step 3 above>
|
||||
|
||||
"$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad"
|
||||
|
||||
"$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad"
|
||||
```
|
||||
|
||||
## Adjust the Project Settings
|
||||
|
||||
* Turn on Separate Strip,
|
||||
* Set the Strip Style to Non-Global Symbols.
|
||||
|
||||
## Write Code!
|
||||
|
||||
You'll need to have an object that acts as the delegate for NSApplication.
|
||||
Inside this object's header, you'll need to add
|
||||
|
||||
1. add an ivar for Breakpad and
|
||||
2. a declaration for the applicationShouldTerminate:(NSApplication`*` sender)
|
||||
message.
|
||||
|
||||
```
|
||||
#import <Breakpad/Breakpad.h>
|
||||
|
||||
@interface BreakpadTest : NSObject {
|
||||
.
|
||||
.
|
||||
.
|
||||
BreakpadRef breakpad;
|
||||
.
|
||||
.
|
||||
.
|
||||
}
|
||||
.
|
||||
.
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||
.
|
||||
.
|
||||
@end
|
||||
```
|
||||
|
||||
Inside your object's implementation file,
|
||||
|
||||
1. add the following method InitBreakpad
|
||||
2. modify your awakeFromNib method to look like the one below,
|
||||
3. modify/add your application's delegate method to look like the one below
|
||||
|
||||
```
|
||||
static BreakpadRef InitBreakpad(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
BreakpadRef breakpad = 0;
|
||||
NSDictionary *plist = [[NSBundle mainBundle] infoDictionary];
|
||||
if (plist) {
|
||||
// Note: version 1.0.0.4 of the framework changed the type of the argument
|
||||
// from CFDictionaryRef to NSDictionary * on the next line:
|
||||
breakpad = BreakpadCreate(plist);
|
||||
}
|
||||
[pool release];
|
||||
return breakpad;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
breakpad = InitBreakpad();
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
BreakpadRelease(breakpad);
|
||||
return NSTerminateNow;
|
||||
}
|
||||
```
|
||||
|
||||
## Configure Breakpad
|
||||
|
||||
Configure Breakpad for your application.
|
||||
|
||||
1. Take a look inside the Breakpad.framework at the Breakpad.h file for the
|
||||
keys, default values, and descriptions to be passed to BreakpadCreate().
|
||||
2. Add/Edit the Breakpad specific entries in the dictionary passed to
|
||||
BreakpadCreate() -- typically your application's info plist.
|
||||
|
||||
Example from the Notifier Info.plist:
|
||||
`<key>BreakpadProduct</key><string>Google_Notifier_Mac</string>
|
||||
<key>BreakpadProductDisplay</key><string>${PRODUCT_NAME}</string>
|
||||
`
|
||||
|
||||
## Build Your Application
|
||||
|
||||
Almost done!
|
||||
|
||||
## Verify
|
||||
|
||||
Double-check:
|
||||
|
||||
Your app should have in its package contents:
|
||||
myApp.app/Contents/Frameworks/Breakpad.framework.
|
||||
|
||||
The symbol files have reasonable contents (you can look at them with a text
|
||||
editor.)
|
||||
|
||||
Look again at the Copy Frameworks phase of your project. Are you leaking .h
|
||||
files? Select them and delete them. (If you drag a bunch of files into your
|
||||
project, Xcode often wants to copy your .h files into the build, revealing
|
||||
Google secrets. Be vigilant!)
|
||||
|
||||
## Upload the symbol file
|
||||
|
||||
You'll need to configure your build process to store symbols in a location that
|
||||
is accessible by the minidump processor. There is a tool in tools/mac/symupload
|
||||
that can be used to send the symbol file via HTTP post.
|
||||
|
||||
1. Test
|
||||
|
||||
Configure breakpad to send reports to a URL by adding to your app's Info.plist:
|
||||
|
||||
```
|
||||
<key>BreakpadURL</key>
|
||||
<string>upload URL</string>
|
||||
<key>BreakpadReportInterval</key>
|
||||
<string>30</string>
|
||||
```
|
||||
|
||||
## Final Notes
|
||||
|
||||
Breakpad checks whether it is being run under a debugger, and if so, normally
|
||||
does nothing. But, you can force Breakpad to function under a debugger by
|
||||
setting the Unix shell variable BREAKPAD\_IGNORE\_DEBUGGER to a non-zero value.
|
||||
You can bracket the source code in the above Write The Code step with #if DEBUG
|
||||
to completely eliminate it from Debug builds. See
|
||||
//depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process
|
||||
forks(), exception handlers are reset to the default for child processes. So
|
||||
they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's
|
||||
Crash Reporter.
|
@ -1,84 +0,0 @@
|
||||
# Breakpad Crash Reporting for Mozilla
|
||||
|
||||
* January 24, 2007
|
||||
* Links updated February 14, 2007
|
||||
* Mozilla HQ
|
||||
* Mark Mentovai
|
||||
* Brian Ryner
|
||||
|
||||
## What is a crash reporter?
|
||||
|
||||
* Enables developers to analyze crashes that occur in the wild
|
||||
* Produces stack backtraces that help identify how a program failed
|
||||
* Offers higher-level data aggregation (topcrashes, MTBF statistics)
|
||||
|
||||
## Motivation
|
||||
|
||||
* Talkback is proprietary and unmaintained
|
||||
* Smaller open-source projects have few options
|
||||
* Larger projects need flexibility and scalability
|
||||
|
||||
## Design Options
|
||||
|
||||
* Stackwalking done on client
|
||||
* Apple CrashReporter
|
||||
* GNOME BugBuddy
|
||||
* Client sends memory dump
|
||||
* Talkback
|
||||
* Windows Error Reporting
|
||||
* Breakpad
|
||||
|
||||
## Goals
|
||||
|
||||
* Provide libraries around which systems can be based
|
||||
* Open-source
|
||||
* Cross-platform
|
||||
* Mac OS X x86, PowerPC
|
||||
* Linux x86
|
||||
* Windows x86
|
||||
* No requirement to distribute symbols
|
||||
|
||||
## Client Libraries
|
||||
|
||||
* Exception handler installed at application startup
|
||||
* Spawns a separate thread
|
||||
* Minidump file written at crash time
|
||||
* Format used by Windows debuggers
|
||||
* Separate application invoked to send
|
||||
* HTTP[S](S.md) POST, can include additional parameters
|
||||
|
||||
## Symbols
|
||||
|
||||
* Cross-platform symbol file format
|
||||
* Contents
|
||||
* Function names
|
||||
* Source file names and line numbers
|
||||
* Windows: Frame pointer omission data
|
||||
* Future: parameters and local variables
|
||||
* Symbol conversion methods
|
||||
|
||||
## Processor
|
||||
|
||||
* Examines minidump file and invokes stackwalker
|
||||
* Symbol files requested from a SymbolSupplier
|
||||
* Produces stack trace
|
||||
* Output may be placed where convenient
|
||||
|
||||
## Intergation
|
||||
|
||||
* Breakpad client present in Gran Paradiso Alpha 1 for Windows
|
||||
* Disabled by default
|
||||
* Enable with `MOZ_AIRBAG`
|
||||
* Proof-of-concept collector
|
||||
* http://mavra.perilith.com/~luser/airbag-collector/list.pl
|
||||
* Other platforms coming soon
|
||||
|
||||
## More Information
|
||||
|
||||
* Project home: http://code.google.com/p/google-breakpad/
|
||||
* Mailing lists
|
||||
* [google-breakpad-dev@googlegroups.com]
|
||||
(http://groups.google.com/group/google-breakpad-dev/)
|
||||
* [google-breakpad-discuss@googlegroups.com]
|
||||
(http://groups.google.com/group/google-breakpad-discuss/)
|
||||
* Ask me (irc.mozilla.org: mento)
|
@ -1,230 +0,0 @@
|
||||
# Breakpad Processor Library
|
||||
|
||||
## Objective
|
||||
|
||||
The Breakpad processor library is an open-source framework to access the the
|
||||
information contained within crash dumps for multiple platforms, and to use that
|
||||
information to produce stack traces showing the call chain of each thread in a
|
||||
process. After processing, this data is made available to users of the library.
|
||||
|
||||
## Background
|
||||
|
||||
The Breakpad processor is intended to sit at the core of a comprehensive
|
||||
crash-reporting system that does not require debugging information to be
|
||||
provided to those running applications being monitored. Some existing
|
||||
crash-reporting systems, such as [GNOME](http://www.gnome.org/)’s Bug-Buddy and
|
||||
[Apple](http://www.apple.com/)’s [CrashReporter]
|
||||
(http://developer.apple.com/technotes/tn2004/tn2123.html), require symbolic
|
||||
information to be present on the end user’s computer; in the case of
|
||||
CrashReporter, the reports are transmitted only to Apple, not to third-party
|
||||
developers. Other systems, such as [Microsoft](http://www.microsoft.com/)’s
|
||||
[Windows Error Reporting](http://msdn.microsoft.com/isv/resources/wer/) and
|
||||
SupportSoft’s Talkback, transmit only a snapshot of a crashed process’ state,
|
||||
which can later be combined with symbolic debugging information without the need
|
||||
for it to be present on end users’ computers. Because symbolic debugging
|
||||
information consumes a large amount of space and is otherwise not needed during
|
||||
the normal operation of software, and because some developers are reluctant to
|
||||
release debugging symbols to their customers, Breakpad follows the latter
|
||||
approach.
|
||||
|
||||
We know of no currently-maintained crash-reporting systems that meet our
|
||||
requirements, which are to: * allow for symbols to be separate from the
|
||||
application, * handle crash reports from multiple platforms, * allow developers
|
||||
to operate their own crash-reporting platform, and to * be open-source. Windows
|
||||
Error Reporting only functions for Microsoft products, and requires the
|
||||
involvement of Microsoft’s servers. Talkback, while cross-platform, has not been
|
||||
maintained and at this point does not support Mac OS X on x86, which we consider
|
||||
to be a significant platform. Talkback is also closed-source commercial
|
||||
software, and has very specific requirements for its server platform.
|
||||
|
||||
We are aware of Windows-only crash-reporting systems that leverage Microsoft’s
|
||||
debugging interfaces. Such systems, even if extended to support dumps from other
|
||||
platforms, are tied to using Windows for at least a portion of the processor
|
||||
platform.
|
||||
|
||||
## Overview
|
||||
|
||||
The Breakpad processor itself is written in standard C++ and will work on a
|
||||
variety of platforms. The dumps it accepts may also have been created on a
|
||||
variety of systems. The library is able to combine dumps with symbolic debugging
|
||||
information to create stack traces that include function signatures. The
|
||||
processor library includes simple command-line tools to examine dumps and
|
||||
process them, producing stack traces. It also exposes several layers of APIs
|
||||
enabling crash-reporting systems to be built around the Breakpad processor.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Dump Files
|
||||
|
||||
In the processor, the dump data is of primary significance. Dumps typically
|
||||
contain:
|
||||
|
||||
* CPU context (register data) as it was at the time the crash occurred, and an
|
||||
indication of which thread caused the crash. General-purpose registers are
|
||||
included, as are special-purpose registers such as the instruction pointer
|
||||
(program counter).
|
||||
* Information about each thread of execution within a crashed process,
|
||||
including:
|
||||
* The memory region used for each thread’s stack.
|
||||
* CPU context for each thread, which for various reasons is not the same
|
||||
as the crash context in the case of the crashed thread.
|
||||
* A list of loaded code segments (or modules), including:
|
||||
* The name of the file (`.so`, `.exe`, `.dll`, etc.) which provides the
|
||||
code.
|
||||
* The boundaries of the memory region in which the code segment is visible
|
||||
to the process.
|
||||
* A reference to the debugging information for the code module, when such
|
||||
information is available.
|
||||
|
||||
Ordinarily, dumps are produced as a result of a crash, but other triggers may be
|
||||
set to produce dumps at any time a developer deems appropriate. The Breakpad
|
||||
processor can handle dumps in the minidump format, either generated by an
|
||||
[Breakpad client “handler”](client_design.md) implementation, or by another
|
||||
implementation that produces dumps in this format. The
|
||||
[DbgHelp.dll!MiniDumpWriteDump]
|
||||
(http://msdn2.microsoft.com/en-us/library/ms680360.aspx) function on Windows
|
||||
produces dumps in this format, and is the basis for the Breakpad handler
|
||||
implementation on that platform.
|
||||
|
||||
The [minidump format]
|
||||
(http://msdn.microsoft.com/en-us/library/ms679293%28VS.85%29.aspx) is
|
||||
essentially a simple container format, organized as a series of streams. Each
|
||||
stream contains some type of data relevant to the crash. A typical “normal”
|
||||
minidump contains streams for the thread list, the module list, the CPU context
|
||||
at the time of the crash, and various bits of additional system information.
|
||||
Other types of minidump can be generated, such as a full-memory minidump, which
|
||||
in addition to stack memory contains snapshots of all of a process’ mapped
|
||||
memory regions.
|
||||
|
||||
The minidump format was chosen as Breakpad’s dump format because it has an
|
||||
established track record on Windows, and it can be adapted to meet the needs of
|
||||
the other platforms that Breakpad supports. Most other operating systems use
|
||||
“core” files as their native dump formats, but the capabilities of core files
|
||||
vary across platforms, and because core files are usually presented in a
|
||||
platform’s native executable format, there are complications involved in
|
||||
accessing the data contained therein without the benefit of the header files
|
||||
that define an executable format’s entire structure. Because minidumps are
|
||||
leaner than a typical executable format, a redefinition of the format in a
|
||||
cross-platform header file, `minidump_format.h`, was a straightforward task.
|
||||
Similarly, the capabilities of the minidump format are understood, and because
|
||||
it provides an extensible container, any of Breakpad’s needs that could not be
|
||||
met directly by the standard minidump format could likely be met by extending it
|
||||
as needed. Finally, using this format means that the dump file is compatible
|
||||
with native debugging tools at least on Windows. A possible future avenue for
|
||||
exploration is the conversion of minidumps to core files, to enable this same
|
||||
benefit on other platforms.
|
||||
|
||||
We have already provided an extension to the minidump format that allows it to
|
||||
carry dumps generated on systems with PowerPC processors. The format already
|
||||
allows for variable CPUs, so our work in this area was limited to defining a
|
||||
context structure sufficient to represent the execution state of a PowerPC. We
|
||||
have also defined an extension that allows minidumps to indicate which thread of
|
||||
execution requested a dump be produced for non-crash dumps.
|
||||
|
||||
Often, the information contained within a dump alone is sufficient to produce a
|
||||
full stack backtrace for each thread. Certain optimizations that compilers
|
||||
employ in producing code frustrate this process. Specifically, the “frame
|
||||
pointer omission” optimization of x86 compilers can make it impossible to
|
||||
produce useful stack traces given only a stack snapshot and CPU context. In
|
||||
these cases, however, compiler-emitted debugging information can aid in
|
||||
producing useful stack traces. The Breakpad processor is able to take advantage
|
||||
of this debugging information as supplied by Microsoft’s C/C++ compiler, the
|
||||
only compiler to apply such optimizations by default. As a result, the Breakpad
|
||||
processor can produce useful stack traces even from code with frame pointer
|
||||
omission optimizations as produced by this compiler.
|
||||
|
||||
### Symbol Files
|
||||
|
||||
The [symbol files](symbol_files.md) that the Breakpad processor accepts allow
|
||||
for frame pointer omission data, but this is only one of their capabilities.
|
||||
Each symbol file also includes information about the functions, source files,
|
||||
and source code line numbers for a single module of code. A module is an
|
||||
individually-loadble chunk of code: these can be executables containing a main
|
||||
program (`exe` files on Windows) or shared libraries (`.so` files on Linux,
|
||||
`.dylib` files, frameworks, and bundles on Mac OS X, and `.dll` files on
|
||||
Windows). Dumps contain information about which of these modules were loaded at
|
||||
the time the dump was produced, and given this information, the Breakpad
|
||||
processor attempts to locate debugging symbols for the module through a
|
||||
user-supplied function embodied in a “symbol supplier.” Breakpad includes a
|
||||
sample symbol supplier, called `SimpleSymbolSupplier`, that is used by its
|
||||
command-line tools; this supplier locates symbol files by pathname.
|
||||
`SimpleSymbolSupplier` is also available to other users of the Breakpad
|
||||
processor library. This allows for the use of a simple reference implementation,
|
||||
but preserves flexibility for users who may have more demanding symbol file
|
||||
storage needs.
|
||||
|
||||
Breakpad’s symbol file format is text-based, and was defined to be fairly
|
||||
human-readable and to encompass the needs of multiple platforms. The Breakpad
|
||||
processor itself does not operate directly with native symbol formats ([DWARF]
|
||||
(http://dwarf.freestandards.org/) and [STABS]
|
||||
(http://sourceware.org/gdb/current/onlinedocs/stabs.html) on most Unix-like
|
||||
systems, [.pdb files]
|
||||
(http://msdn2.microsoft.com/en-us/library/yd4f8bd1(VS.80).aspx) on Windows),
|
||||
because of the complications in accessing potentially complex symbol formats
|
||||
with slight variations between platforms, stored within different types of
|
||||
binary formats. In the case of `.pdb` files, the debugging format is not even
|
||||
documented. Instead, Breakpad’s symbol files are produced on each platform,
|
||||
using specific debugging APIs where available, to convert native symbols to
|
||||
Breakpad’s cross-platform format.
|
||||
|
||||
### Processing
|
||||
|
||||
Most commonly, a developer will enable an application to use Breakpad by
|
||||
building it with a platform-specific [client “handler”](client_design.md)
|
||||
library. After building the application, the developer will create symbol files
|
||||
for Breakpad’s use using the included `dump_syms` or `symupload` tools, or
|
||||
another suitable tool, and place the symbol files where the processor’s symbol
|
||||
supplier will be able to locate them.
|
||||
|
||||
When a dump file is given to the processor’s `MinidumpProcessor` class, it will
|
||||
read it using its included minidump reader, contained in the `Minidump` family
|
||||
of classes. It will collect information about the operating system and CPU that
|
||||
produced the dump, and determine whether the dump was produced as a result of a
|
||||
crash or at the direct request of the application itself. It then loops over all
|
||||
of the threads in a process, attempting to walk the stack associated with each
|
||||
thread. This process is achieved by the processor’s `Stackwalker` components, of
|
||||
which there are a slightly different implementations for each CPU type that the
|
||||
processor is able to handle dumps from. Beginning with a thread’s context, and
|
||||
possibly using debugging data, the stackwalker produces a list of stack frames,
|
||||
containing each instruction executed in the chain. These instructions are
|
||||
matched up with the modules that contributed them to a process, and the
|
||||
`SymbolSupplier` is invoked to locate a symbol file. The symbol file is given to
|
||||
a `SourceLineResolver`, which matches the instruction up with a specific
|
||||
function name, source file, and line number, resulting in a representation of a
|
||||
stack frame that can easily be used to identify which code was executing.
|
||||
|
||||
The results of processing are made available in a `ProcessState` object, which
|
||||
contains a vector of threads, each containing a vector of stack frames.
|
||||
|
||||
For small-scale use of the Breakpad processor, and for testing and debugging,
|
||||
the `minidump_stackwalk` tool is provided. It invokes the processor and displays
|
||||
the full results of processing, optionally allowing symbols to be provided to
|
||||
the processor by a pathname-based symbol supplier, `SimpleSymbolSupplier`.
|
||||
|
||||
For lower-level testing and debugging, the processor library also includes a
|
||||
`minidump_dump` tool, which walks through an entire minidump file and displays
|
||||
its contents in somewhat readable form.
|
||||
|
||||
### Platform Support
|
||||
|
||||
The Breakpad processor library is able to process dumps produced on Mac OS X
|
||||
systems running on x86, x86-64, and PowerPC processors, on Windows and Linux
|
||||
systems running on x86 or x86-64 processors, and on Android systems running ARM
|
||||
or x86 processors. The processor library itself is written in standard C++, and
|
||||
should function properly in most Unix-like environments. It has been tested on
|
||||
Linux and Mac OS X.
|
||||
|
||||
## Future Plans
|
||||
|
||||
There are currently no firm plans or timetables to implement any of these
|
||||
features, although they are possible avenues for future exploration.
|
||||
|
||||
The symbol file format can be extended to carry information about the locations
|
||||
of parameters and local variables as stored in stack frames and registers, and
|
||||
the processor can use this information to provide enhanced stack traces showing
|
||||
function arguments and variable values.
|
||||
|
||||
On Mac OS X and Linux, we can provide tools to convert files from the minidump
|
||||
format into the native core format. This will enable developers to open dump
|
||||
files in a native debugger, just as they are presently able to do with minidumps
|
||||
on Windows.
|
@ -1,160 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
This page aims to provide a detailed description of how Breakpad produces stack
|
||||
traces from the information contained within a minidump file.
|
||||
|
||||
# Details
|
||||
|
||||
## Starting the Process
|
||||
|
||||
Typically the stack walking process is initiated by instantiating the
|
||||
[MinidumpProcessor]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc)
|
||||
class and calling the [MinidumpProcessor::Process]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc#61)
|
||||
method, providing it a minidump file to process. To produce a useful stack
|
||||
trace, the MinidumpProcessor requires two other objects which are passed in its
|
||||
constructor: a [SymbolSupplier]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h)
|
||||
and a [SourceLineResolverInterface]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h).
|
||||
The SymbolSupplier object is responsible for locating and providing SymbolFiles
|
||||
that match modules from the minidump. The SourceLineResolverInterface is
|
||||
responsible for loading the symbol files and using the information contained
|
||||
within to provide function and source information for stack frames, as well as
|
||||
information on how to unwind from a stack frame to its caller. More detail will
|
||||
be provided on these interactions later.
|
||||
|
||||
A number of data streams are extracted from the minidump to begin stack walking:
|
||||
the list of threads from the process ([MinidumpThreadList]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#335)),
|
||||
the list of modules loaded in the process ([MinidumpModuleList]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#501)),
|
||||
and information about the exception that caused the process to crash
|
||||
([MinidumpException]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#615)).
|
||||
|
||||
## Enumerating Threads
|
||||
|
||||
For each thread in the thread list ([MinidumpThread]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#299)),
|
||||
the thread memory containing the stack for the thread ([MinidumpMemoryRegion]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#236))
|
||||
and the CPU context representing the CPU state of the thread at the time the
|
||||
dump was written ([MinidumpContext]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#171))
|
||||
are extracted from the minidump. If the thread being processed is the thread
|
||||
that produced the exception then a CPU context is obtained from the
|
||||
MinidumpException object instead, which represents the CPU state of the thread
|
||||
at the point of the exception. A stack walker is then instantiated by calling
|
||||
the [Stackwalker::StackwalkerForCPU]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#77)
|
||||
method and passing it the CPU context, the thread memory, the module list, as
|
||||
well as the SymbolSupplier and SourceLineResolverInterface. This method selects
|
||||
the specific !Stackwalker subclass based on the CPU architecture of the provided
|
||||
CPU context and returns an instance of that subclass.
|
||||
|
||||
## Walking a thread's stack
|
||||
|
||||
Once a !Stackwalker instance has been obtained, the processor calls the
|
||||
[Stackwalker::Walk]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h)
|
||||
method to obtain a list of frames representing the stack of this thread. The
|
||||
!Stackwalker starts by calling the GetContextFrame method which returns a
|
||||
StackFrame representing the top of the stack, with CPU state provided by the
|
||||
initial CPU context. From there, the stack walker repeats the following steps
|
||||
for each frame in turn:
|
||||
|
||||
### Finding the Module
|
||||
|
||||
The address of the instruction pointer of the current frame is used to determine
|
||||
which module contains the current frame by calling the module list's
|
||||
[GetModuleForAddress]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/code_modules.h#56)
|
||||
method.
|
||||
|
||||
### Locating Symbols
|
||||
|
||||
If a module is located, the SymbolSupplier is asked to locate symbols
|
||||
corresponding to the module by calling its [GetCStringSymbolData]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h#87)
|
||||
method. Typically this is implemented by using the module's debug filename (the
|
||||
PDB filename for Windows dumps) and debug identifier (a GUID plus one extra
|
||||
digit) as a lookup key. The [SimpleSymbolSupplier]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/simple_symbol_supplier.cc)
|
||||
class simply uses these as parts of a file path to locate a flat file on disk.
|
||||
|
||||
### Loading Symbols
|
||||
|
||||
If a symbol file is located, the SourceLineResolverInterface is then asked to
|
||||
load the symbol file by calling its [LoadModuleUsingMemoryBuffer]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#71)
|
||||
method. The [BasicSourceLineResolver]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/basic_source_line_resolver.cc)
|
||||
implementation parses the text-format [symbol file](symbol_files.md) into
|
||||
in-memory data structures to make lookups by address of function names, source
|
||||
line information, and unwind information easy.
|
||||
|
||||
### Getting source line information
|
||||
|
||||
If a symbol file has been successfully loaded, the SourceLineResolverInterface's
|
||||
[FillSourceLineInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#89)
|
||||
method is called to provide a function name and source line information for the
|
||||
current frame. This is done by subtracting the base address of the module
|
||||
containing the current frame from the instruction pointer of the current frame
|
||||
to obtain a relative virtual address (RVA), which is a code offset relative to
|
||||
the start of the module. This RVA is then used as a lookup into a table of
|
||||
functions ([FUNC lines](SymbolFiles#FUNC_records.md) from the symbol file), each
|
||||
of which has an associated address range (function start address, function
|
||||
size). If a function is found whose address range contains the RVA, then its
|
||||
name is used. The RVA is then used as a lookup into a table of source lines
|
||||
([line records](SymbolFiles#Line_records.md) from the symbol file), each of
|
||||
which also has an associated address range. If a match is found it will provide
|
||||
the file name and source line associated with the current frame. If no match was
|
||||
found in the function table, another table of publicly exported symbols may be
|
||||
consulted ([PUBLIC lines](SymbolFiles#PUBLIC_records.md) from the symbol file).
|
||||
Public symbols contain only a start address, so the lookup simply looks for the
|
||||
nearest symbol that is less than the provided RVA.
|
||||
|
||||
### Finding the caller frame
|
||||
|
||||
To find the next frame in the stack, the !Stackwalker calls its [GetCallerFrame]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#186)
|
||||
method, passing in the current frame. Each !Stackwalker subclass implements
|
||||
GetCallerFrame differently, but there are common patterns.
|
||||
|
||||
Typically the first step is to query the SourceLineResolverInterface for the
|
||||
presence of detailed unwind information. This is done using its
|
||||
[FindWindowsFrameInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#96)
|
||||
and [FindCFIFrameInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#102)
|
||||
methods. These methods look for Windows unwind info extracted from a PDB file
|
||||
([STACK WIN](SymbolFiles#STACK_WIN_records.md) lines from the symbol file), or
|
||||
DWARF CFI extracted from a binary ([STACK CFI](SymbolFiles#STACK_CFI_records.md)
|
||||
lines from the symbol file) respectively. The information covers address ranges,
|
||||
so the RVA of the current frame is used for lookup as with function and source
|
||||
line information.
|
||||
|
||||
If unwind info is found it provides a set of rules to recover the register state
|
||||
of the caller frame given the current register state as well as the thread's
|
||||
stack memory. The rules are evaluated to produce the caller frame.
|
||||
|
||||
If unwind info is not found then the !Stackwalker may resort to other methods.
|
||||
Typically on architectures which specify a frame pointer unwinding by
|
||||
dereferencing the frame pointer is tried next. If that is successful it is used
|
||||
to produce the caller frame.
|
||||
|
||||
If no caller frame was found by any other method most !Stackwalker
|
||||
implementations resort to stack scanning by looking at each word on the stack
|
||||
down to a fixed depth (implemented in the [Stackwalker::ScanForReturnAddress]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#131)
|
||||
method) and using a heuristic to attempt to find a reasonable return address
|
||||
(implemented in the [Stackwalker::InstructionAddressSeemsValid]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#111)
|
||||
method).
|
||||
|
||||
If no caller frame is found or the caller frame seems invalid, stack walking
|
||||
stops. If a caller frame was found then these steps repeat using the new frame
|
||||
as the current frame.
|
@ -1,497 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
Given a minidump file, the Breakpad processor produces stack traces that include
|
||||
function names and source locations. However, minidump files contain only the
|
||||
byte-by-byte contents of threads' registers and stacks, without function names
|
||||
or machine-code-to-source mapping data. The processor consults Breakpad symbol
|
||||
files for the information it needs to produce human-readable stack traces from
|
||||
the binary-only minidump file.
|
||||
|
||||
The platform-specific symbol dumping tools parse the debugging information the
|
||||
compiler provides (whether as DWARF or STABS sections in an ELF file or as
|
||||
stand-alone PDB files), and write that information back out in the Breakpad
|
||||
symbol file format. This format is much simpler and less detailed than compiler
|
||||
debugging information, and values legibility over compactness.
|
||||
|
||||
# Overview
|
||||
|
||||
Breakpad symbol files are ASCII text files, with lines delimited as appropriate
|
||||
for the host platform. Each line is a _record_, divided into fields by single
|
||||
spaces; in some cases, the last field of the record can contain spaces. The
|
||||
first field is a string indicating what sort of record the line represents
|
||||
(except for line records; these are very common, making them the default saves
|
||||
space). Some fields hold decimal or hexadecimal numbers; hexadecimal numbers
|
||||
have no "0x" prefix, and use lower-case letters.
|
||||
|
||||
Breakpad symbol files contain the following record types. With some
|
||||
restrictions, these may appear in any order.
|
||||
|
||||
* A `MODULE` record describes the executable file or shared library from which
|
||||
this data was derived, for use by symbol suppliers. A `MODULE' record should
|
||||
be the first record in the file.
|
||||
|
||||
* A `FILE` record gives a source file name, and assigns it a number by which
|
||||
other records can refer to it.
|
||||
|
||||
* A `FUNC` record describes a function present in the source code.
|
||||
|
||||
* A line record indicates to which source file and line a given range of
|
||||
machine code should be attributed. The line is attributed to the function
|
||||
defined by the most recent `FUNC` record.
|
||||
|
||||
* A `PUBLIC` record gives the address of a linker symbol.
|
||||
|
||||
* A `STACK` record provides information necessary to produce stack traces.
|
||||
|
||||
# `MODULE` records
|
||||
|
||||
A `MODULE` record provides meta-information about the module the symbol file
|
||||
describes. It has the form:
|
||||
|
||||
> `MODULE` _operatingsystem_ _architecture_ _id_ _name_
|
||||
|
||||
For example: `MODULE Linux x86 D3096ED481217FD4C16B29CD9BC208BA0 firefox-bin
|
||||
` These records provide meta-information about the executable or shared library
|
||||
from which this symbol file was generated. A symbol supplier might use this
|
||||
information to find the correct symbol files to use to interpret a given
|
||||
minidump, or to perform other sorts of validation. If present, a `MODULE` record
|
||||
should be the first line in the file.
|
||||
|
||||
The fields are separated by spaces, and cannot contain spaces themselves, except
|
||||
for _name_.
|
||||
|
||||
* The _operatingsystem_ field names the operating system on which the
|
||||
executable or shared library was intended to run. This field should have one
|
||||
of the following values: | **Value** | **Meaning** |
|
||||
|:----------|:--------------------| | Linux | Linux | | mac | Macintosh OSX
|
||||
| | windows | Microsoft Windows |
|
||||
|
||||
* The _architecture_ field indicates what processor architecture the
|
||||
executable or shared library contains machine code for. This field should
|
||||
have one of the following values: | **Value** | **Instruction Set
|
||||
Architecture** | |:----------|:---------------------------------| | x86 |
|
||||
Intel IA-32 | | x86\_64 | AMD64/Intel 64 | | ppc | 32-bit PowerPC | | ppc64
|
||||
| 64-bit PowerPC | | unknown | unknown |
|
||||
|
||||
* The _id_ field is a sequence of hexadecimal digits that identifies the exact
|
||||
executable or library whose contents the symbol file describes. The way in
|
||||
which it is computed varies from platform to platform.
|
||||
|
||||
* The _name_ field contains the base name (the final component of the
|
||||
directory path) of the executable or library. It may contain spaces, and
|
||||
extends to the end of the line.
|
||||
|
||||
# `FILE` records
|
||||
|
||||
A `FILE` record holds a source file name for other records to refer to. It has
|
||||
the form:
|
||||
|
||||
> `FILE` _number_ _name_
|
||||
|
||||
For example: `FILE 2 /home/jimb/mc/in/browser/app/nsBrowserApp.cpp
|
||||
`
|
||||
|
||||
A `FILE` record provides the name of a source file, and assigns it a number
|
||||
which other records (line records, in particular) can use to refer to that file
|
||||
name. The _number_ field is a decimal number. The _name_ field is the name of
|
||||
the file; it may contain spaces.
|
||||
|
||||
# `FUNC` records
|
||||
|
||||
A `FUNC` record describes a source-language function. It has the form:
|
||||
|
||||
> `FUNC` _address_ _size_ _parameter\_size_ _name_
|
||||
|
||||
For example: `FUNC c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&,
|
||||
void**) const
|
||||
`
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||
address and length in bytes of the machine code instructions the function
|
||||
occupies. (Breakpad symbol files cannot accurately describe functions whose code
|
||||
is not contiguous.) The start address is relative to the module's load address.
|
||||
|
||||
The _parameter\_size_ field is a hexadecimal number indicating the size, in
|
||||
bytes, of the arguments pushed on the stack for this function. Some calling
|
||||
conventions, like the Microsoft Windows `stdcall` convention, require the called
|
||||
function to pop parameters passed to it on the stack from its caller before
|
||||
returning. The stack walker uses this value, along with data from `STACK`
|
||||
records, to step from the called function's frame to the caller's frame.
|
||||
|
||||
The _name_ field is the name of the function. In languages that use linker
|
||||
symbol name mangling like C++, this should be the source language name (the
|
||||
"unmangled" form). This field may contain spaces.
|
||||
|
||||
# Line records
|
||||
|
||||
A line record describes the source file and line number to which a given range
|
||||
of machine code should be attributed. It has the form:
|
||||
|
||||
> _address_ _size_ _line_ _filenum_
|
||||
|
||||
For example: `c184 7 59 4
|
||||
`
|
||||
|
||||
Because they are so common, line records do not begin with a string indicating
|
||||
the record type. All other record types' names use upper-case letters;
|
||||
hexadecimal numbers, like a line record's _address_, use lower-case letters.
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||
address and length in bytes of the machine code. The address is relative to the
|
||||
module's load address.
|
||||
|
||||
The _line_ field is the line number to which the machine code should be
|
||||
attributed, in decimal; the first line of the source file is line number 1. The
|
||||
_filenum_ field is a decimal number appearing in a prior `FILE` record; the name
|
||||
given in that record is the source file name for the machine code.
|
||||
|
||||
The line is assumed to belong to the function described by the last preceding
|
||||
`FUNC` record. Line records may not appear before the first `FUNC' record.
|
||||
|
||||
No two line records in a symbol file cover the same range of addresses. However,
|
||||
there may be many line records with identical line and file numbers, as a given
|
||||
source line may contribute many non-contiguous blocks of machine code.
|
||||
|
||||
# `PUBLIC` records
|
||||
|
||||
A `PUBLIC` record describes a publicly visible linker symbol, such as that used
|
||||
to identify an assembly language entry point or region of memory. It has the
|
||||
form:
|
||||
|
||||
> PUBLIC _address_ _parameter\_size_ _name_
|
||||
|
||||
For example: `PUBLIC 2160 0 Public2_1
|
||||
`
|
||||
|
||||
The Breakpad processor essentially treats a `PUBLIC` record as defining a
|
||||
function with no line number data and an indeterminate size: the code extends to
|
||||
the next address mentioned. If a given address is covered by both a `PUBLIC`
|
||||
record and a `FUNC` record, the processor uses the `FUNC` data.
|
||||
|
||||
The _address_ field is a hexadecimal number indicating the symbol's address,
|
||||
relative to the module's load address.
|
||||
|
||||
The _parameter\_size_ field is a hexadecimal number indicating the size of the
|
||||
parameters passed to the code whose entry point the symbol marks, if known. This
|
||||
field has the same meaning as the _parameter\_size_ field of a `FUNC` record;
|
||||
see that description for more details.
|
||||
|
||||
The _name_ field is the name of the symbol. In languages that use linker symbol
|
||||
name mangling like C++, this should be the source language name (the "unmangled"
|
||||
form). This field may contain spaces.
|
||||
|
||||
# `STACK WIN` records
|
||||
|
||||
Given a stack frame, a `STACK WIN` record indicates how to find the frame that
|
||||
called it. It has the form:
|
||||
|
||||
> STACK WIN _type_ _rva_ _code\_size_ _prologue\_size_ _epilogue\_size_
|
||||
> _parameter\_size_ _saved\_register\_size_ _local\_size_ _max\_stack\_size_
|
||||
> _has\_program\_string_ _program\_string\_OR\_allocates\_base\_pointer_
|
||||
|
||||
For example: `STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + =
|
||||
$ebp $ebp ^ =
|
||||
`
|
||||
|
||||
All fields of a `STACK WIN` record, except for the last, are hexadecimal
|
||||
numbers.
|
||||
|
||||
The _type_ field indicates what sort of stack frame data this record holds. Its
|
||||
value should be one of the values of the [StackFrameTypeEnum]
|
||||
(http://msdn.microsoft.com/en-us/library/bc5207xw%28VS.100%29.aspx) type in
|
||||
Microsoft's [Debug Interface Access (DIA)]
|
||||
(http://msdn.microsoft.com/en-us/library/x93ctkx8%28VS.100%29.aspx) API.
|
||||
Breakpad uses only records of type 4 (`FrameTypeFrameData`) and 0
|
||||
(`FrameTypeFPO`); it ignores others. These types differ only in whether the last
|
||||
field is an _allocates\_base\_pointer_ flag (`FrameTypeFPO`) or a program string
|
||||
(`FrameTypeFrameData`). If more than one record covers a given address, Breakpad
|
||||
prefers `FrameTypeFrameData` records over `FrameTypeFPO` records.
|
||||
|
||||
The _rva_ and _code\_size_ fields give the starting address and length in bytes
|
||||
of the machine code covered by this record. The starting address is relative to
|
||||
the module's load address.
|
||||
|
||||
The _prologue\_size_ and _epilogue\_size_ fields give the length, in bytes, of
|
||||
the prologue and epilogue machine code within the record's range. Breakpad does
|
||||
not use these values.
|
||||
|
||||
The _parameter\_size_ field gives the number of argument bytes this function
|
||||
expects to have been passed. This field has the same meaning as the
|
||||
_parameter\_size_ field of a `FUNC` record; see that description for more
|
||||
details.
|
||||
|
||||
The _saved\_register\_size_ field gives the number of bytes in the stack frame
|
||||
dedicated to preserving the values of any callee-saves registers used by this
|
||||
function.
|
||||
|
||||
The _local\_size_ field gives the number of bytes in the stack frame dedicated
|
||||
to holding the function's local variables and temporary values.
|
||||
|
||||
The _max\_stack\_size_ field gives the maximum number of bytes pushed on the
|
||||
stack in the frame. Breakpad does not use this value.
|
||||
|
||||
If the _has\_program\_string_ field is zero, then the `STACK WIN` record's final
|
||||
field is an _allocates\_base\_pointer_ flag, as a hexadecimal number; this is
|
||||
expected for records whose _type_ is 0. Otherwise, the final field is a program
|
||||
string.
|
||||
|
||||
## Interpreting a `STACK WIN` record
|
||||
|
||||
Given the register values for a frame F, we can find the calling frame as
|
||||
follows:
|
||||
|
||||
* If the _has\_program\_string_ field of a `STACK WIN` record is zero, then
|
||||
the final field is _allocates\_base\_pointer_, a flag indicating whether the
|
||||
frame uses the frame pointer register, `%ebp`, as a general-purpose
|
||||
register.
|
||||
* If _allocates\_base\_pointer_ is true, then `%ebp` does not point to the
|
||||
frame's base address. Instead,
|
||||
* Let _next\_parameter\_size_ be the parameter size of the function
|
||||
frame F called (**not** this record's _parameter\_size_ field), or
|
||||
zero if F is the youngest frame on the stack. You must find this
|
||||
value in F's callee's `FUNC`, `STACK WIN`, or `PUBLIC` records.
|
||||
* Let _frame\_size_ be the sum of the _local\_size_ field, the
|
||||
_saved\_register\_size_ field, and _next\_parameter\_size_. > > With
|
||||
those definitions in place, we can recover the calling frame as
|
||||
follows:
|
||||
* F's return address is at `%esp +`_frame\_size_,
|
||||
* the caller's value of `%ebp` is saved at `%esp
|
||||
+`_next\_parameter\_size_`+`_saved\_register\_size_`- 8`, and
|
||||
* the caller's value of `%esp` just before the call instruction was
|
||||
`%esp +`_frame\_size_`+ 4`. > > (Why do we include
|
||||
_next\_parameter\_size_ in the sum when computing _frame\_size_ and
|
||||
the address of the saved `%ebp`? When a function A has called a
|
||||
function B, the arguments that A pushed for B are considered part of
|
||||
A's stack frame: A's value for `%esp` points at the last argument
|
||||
pushed for B. Thus, we must include the size of those arguments
|
||||
(given by the debugging info for B) along with the size of A's
|
||||
register save area and local variable area (given by the debugging
|
||||
info for A) when computing the overall size of A's frame.)
|
||||
* If _allocates\_base\_pointer_ is false, then F's function doesn't use
|
||||
`%ebp` at all. You may recover the calling frame as above, except that
|
||||
the caller's value of `%ebp` is the same as F's value for `%ebp`, so no
|
||||
steps are necessary to recover it.
|
||||
* If the _has\_program\_string_ field of a `STACK WIN` record is not zero,
|
||||
then the record's final field is a string containing a program to be
|
||||
interpreted to recover the caller's frame. The comments in the
|
||||
[postfix\_evaluator.h]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/postfix_evaluator.h#40)
|
||||
header file explain the language in which the program is written. You should
|
||||
place the following variables in the dictionary before interpreting the
|
||||
program:
|
||||
* `$ebp` and `$esp` should be the values of the `%ebp` and `%esp`
|
||||
registers in F.
|
||||
* `.cbParams`, `.cbSavedRegs`, and `.cbLocals`, should be the values of
|
||||
the `STACK WIN` record's _parameter\_size_, _saved\_register\_size_, and
|
||||
_local\_size_ fields.
|
||||
* `.raSearchStart` should be set to the address on the stack to begin
|
||||
scanning for a return address, if necessary. The Breakpad processor sets
|
||||
this to the value of `%esp` in F, plus the _frame\_size_ value mentioned
|
||||
above.
|
||||
|
||||
> If the program stores values for `$eip`, `$esp`, `$ebp`, `$ebx`, `$esi`, or
|
||||
> `$edi`, then those are the values of the given registers in the caller. If the
|
||||
> value of `$eip` is zero, that indicates that the end of the stack has been
|
||||
> reached.
|
||||
|
||||
The Breakpad processor checks that the value yielded by the above for the
|
||||
calling frame's instruction address refers to known code; if the address seems
|
||||
to be bogus, then it uses a heuristic search to find F's return address and
|
||||
stack base.
|
||||
|
||||
# `STACK CFI` records
|
||||
|
||||
`STACK CFI` ("Call Frame Information") records describe how to walk the stack
|
||||
when execution is at a given machine instruction. These records take one of two
|
||||
forms:
|
||||
|
||||
> `STACK CFI INIT` _address_ _size_ _register<sub>1</sub>_:
|
||||
> _expression<sub>1</sub>_ _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
|
||||
>
|
||||
> `STACK CFI` _address_ _register<sub>1</sub>_: _expression<sub>1</sub>_
|
||||
> _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
STACK CFI INIT 804c4b0 40 .cfa: $esp 4 + $eip: .cfa 4 - ^
|
||||
STACK CFI 804c4b1 .cfa: $esp 8 + $ebp: .cfa 8 - ^
|
||||
```
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers. Each
|
||||
_register_<sub>i</sub> is the name of a register or pseudoregister. Each
|
||||
_expression_ is a Breakpad postfix expression, which may contain spaces, but
|
||||
never ends with a colon. (The appropriate register names for a given
|
||||
architecture are determined when `STACK CFI` records are first enabled for that
|
||||
architecture, and should be documented in the appropriate
|
||||
`stackwalker_`_architecture_`.cc` source file.)
|
||||
|
||||
STACK CFI records describe, at each machine instruction in a given function, how
|
||||
to recover the values the machine registers had in the function's caller.
|
||||
Naturally, some registers' values are simply lost, but there are three cases in
|
||||
which they can be recovered:
|
||||
|
||||
* You can always recover the program counter, because that's the function's
|
||||
return address. If the function is ever going to return, the PC must be
|
||||
saved somewhere.
|
||||
|
||||
* You can always recover the stack pointer. The function is responsible for
|
||||
popping its stack frame before it returns to the caller, so it must be able
|
||||
to restore this, as well.
|
||||
|
||||
* You should be able to recover the values of callee-saves registers. These
|
||||
are registers whose values the callee must preserve, either by saving them
|
||||
in its own stack frame before using them and re-loading them before
|
||||
returning, or by not using them at all.
|
||||
|
||||
(As an exception, note that functions which never return may not save any of
|
||||
this data. It may not be possible to walk the stack past such functions' stack
|
||||
frames.)
|
||||
|
||||
Given rules for recovering the values of a function's caller's registers, we can
|
||||
walk up the stack. Starting with the current set of registers --- the PC of the
|
||||
instruction we're currently executing, the current stack pointer, etc. --- we
|
||||
use CFI to recover the values those registers had in the caller of the current
|
||||
frame. This gives us a PC in the caller whose CFI we can look up; we apply the
|
||||
process again to find that function's caller; and so on.
|
||||
|
||||
Concretely, CFI records represent a table with a row for each machine
|
||||
instruction address and a column for each register. The table entry for a given
|
||||
address and register contains a rule describing how, when the PC is at that
|
||||
address, to restore the value that register had in the caller.
|
||||
|
||||
There are some special columns:
|
||||
|
||||
* A column named `.cfa`, for "Canonical Frame Address", tells how to compute
|
||||
the base address of the frame; other entries can refer to the CFA in their
|
||||
rules.
|
||||
|
||||
* A column named `.ra` represents the return address.
|
||||
|
||||
For example, suppose we have a machine with 32-bit registers, one-byte
|
||||
instructions, a stack that grows downwards, and an assembly language that
|
||||
resembles C. Suppose further that we have a function whose machine code looks
|
||||
like this:
|
||||
|
||||
```
|
||||
func: ; entry point; return address at sp
|
||||
func+0: sp -= 16 ; allocate space for stack frame
|
||||
func+1: sp[12] = r0 ; save 4-byte r0 at sp+12
|
||||
... ; stuff that doesn't affect stack
|
||||
func+10: sp -= 4; *sp = x ; push some 4-byte x on the stack
|
||||
... ; stuff that doesn't affect stack
|
||||
func+20: r0 = sp[16] ; restore saved r0
|
||||
func+21: sp += 20 ; pop whole stack frame
|
||||
func+22: pc = *sp; sp += 4 ; pop return address and jump to it
|
||||
```
|
||||
|
||||
The following table would describe the function above:
|
||||
|
||||
**code address** | **.cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **.ra**
|
||||
:--------------- | :------- | :---------------------- | :---------------------- | :-- | :-------
|
||||
func+0 | sp | | | | `cfa[0]`
|
||||
func+1 | sp+16 | | | | `cfa[0]`
|
||||
func+2 | sp+16 | `cfa[-4]` | | | `cfa[0]`
|
||||
func+11 | sp+20 | `cfa[-4]` | | | `cfa[0]`
|
||||
func+21 | sp+20 | | | | `cfa[0]`
|
||||
func+22 | sp | | | | `cfa[0]`
|
||||
|
||||
Some things to note here:
|
||||
|
||||
* Each row describes the state of affairs **before** executing the instruction
|
||||
at the given address. Thus, the row for func+0 describes the state before we
|
||||
execute the first instruction, which allocates the stack frame. In the next
|
||||
row, the formula for computing the CFA has changed, reflecting the
|
||||
allocation.
|
||||
|
||||
* The other entries are written in terms of the CFA; this allows them to
|
||||
remain unchanged as the stack pointer gets bumped around. For example, to
|
||||
find the caller's value for r0 (on Google Code) at func+2, we would first
|
||||
compute the CFA by adding 16 to the sp, and then subtract four from that to
|
||||
find the address at which r0 (on Google Code) was saved.
|
||||
|
||||
* Although the example doesn't show this, most calling conventions designate
|
||||
"callee-saves" and "caller-saves" registers. The callee must restore the
|
||||
values of "callee-saves" registers before returning (if it uses them at
|
||||
all), whereas the callee is free to use "caller-saves" registers without
|
||||
restoring their values. A function that uses caller-saves registers
|
||||
typically does not save their original values at all; in this case, the CFI
|
||||
marks such registers' values as "unrecoverable".
|
||||
|
||||
* Exactly where the CFA points in the frame --- at the return address? below
|
||||
it? At some fixed point within the frame? --- is a question of definition
|
||||
that depends on the architecture and ABI in use. But by definition, the CFA
|
||||
remains constant throughout the lifetime of the frame. It's up to
|
||||
architecture- specific code to know what significance to assign the CFA, if
|
||||
any.
|
||||
|
||||
To save space, the most common type of CFI record only mentions the table
|
||||
entries at which changes take place. So for the above, the CFI data would only
|
||||
actually mention the non-blank entries here:
|
||||
|
||||
**insn** | **cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **ra**
|
||||
:------- | :------ | :---------------------- | :---------------------- | :-- | :-------
|
||||
func+0 | sp | | | | `cfa[0]`
|
||||
func+1 | sp+16 | | | |
|
||||
func+2 | | `cfa[-4]` | | |
|
||||
func+11 | sp+20 | | | |
|
||||
func+21 | | r0 (on Google Code) | | |
|
||||
func+22 | sp | | | |
|
||||
|
||||
A `STACK CFI INIT` record indicates that, at the machine instruction at
|
||||
_address_, belonging to some function, the value that _register<sub>n</sub>_ had
|
||||
in that function's caller can be recovered by evaluating
|
||||
_expression<sub>n</sub>_. The values of any callee-saves registers not mentioned
|
||||
are assumed to be unchanged. (`STACK CFI` records never mention caller-saves
|
||||
registers.) These rules apply starting at _address_ and continue up to, but not
|
||||
including, the address given in the next `STACK CFI` record. The _size_ field is
|
||||
the total number of bytes of machine code covered by this record and any
|
||||
subsequent `STACK CFI` records (until the next `STACK CFI INIT` record). The
|
||||
_address_ field is relative to the module's load address.
|
||||
|
||||
A `STACK CFI` record (no `INIT`) is the same, except that it mentions only those
|
||||
registers whose recovery rules have changed from the previous CFI record. There
|
||||
must be a prior `STACK CFI INIT` or `STACK CFI` record in the symbol file. The
|
||||
_address_ field of this record must be greater than that of the previous record,
|
||||
and it must not be at or beyond the end of the range given by the most recent
|
||||
`STACK CFI INIT` record. The address is relative to the module's load address.
|
||||
|
||||
Each expression is a breakpad-style postfix expression. Expressions may contain
|
||||
spaces, but their tokens may not end with colons. When an expression mentions a
|
||||
register, it refers to the value of that register in the callee, even if a prior
|
||||
name/expression pair gives that register's value in the caller. The exception is
|
||||
`.cfa`, which refers to the canonical frame address computed by the .cfa rule in
|
||||
force at the current instruction.
|
||||
|
||||
The special expression `.undef` indicates that the given register's value cannot
|
||||
be recovered.
|
||||
|
||||
The register names preceding the expressions are always followed by colons. The
|
||||
expressions themselves never contain tokens ending with colons.
|
||||
|
||||
There are two special register names:
|
||||
|
||||
* `.cfa` ("Canonical Frame Address") is the base address of the stack frame.
|
||||
Other registers' rules may refer to this. If no rule is provided for the
|
||||
stack pointer, the value of `.cfa` is the caller's stack pointer.
|
||||
|
||||
* `.ra` is the return address. This is the value of the restored program
|
||||
counter. We use `.ra` instead of the architecture-specific name for the
|
||||
program counter.
|
||||
|
||||
The Breakpad stack walker requires that there be rules in force for `.cfa` and
|
||||
`.ra` at every code address from which it unwinds. If those rules are not
|
||||
present, the stack walker will ignore the `STACK CFI` data, and try to use a
|
||||
different strategy.
|
||||
|
||||
So the CFI for the example function above would be as follows, if `func` were at
|
||||
address 0x1000 (relative to the module's load address):
|
||||
|
||||
```
|
||||
STACK CFI INIT 1000 .cfa: $sp .ra: .cfa ^
|
||||
STACK CFI 1001 .cfa: $sp 16 +
|
||||
STACK CFI 1002 $r0: .cfa 4 - ^
|
||||
STACK CFI 100b .cfa: $sp 20 +
|
||||
STACK CFI 1015 $r0: $r0
|
||||
STACK CFI 1016 .cfa: $sp
|
||||
```
|
@ -1,70 +0,0 @@
|
||||
# Windows Integration overview
|
||||
|
||||
## Windows Client Code
|
||||
|
||||
The Windows client code is in the `src/client/windows` directory of the tree.
|
||||
Since the header files are fairly well commented some specifics are purposely
|
||||
omitted from this document.
|
||||
|
||||
## Integration of minidump-generation
|
||||
|
||||
Once you build the solution inside `src/client/windows`, an output file of
|
||||
`exception_handler.lib` will be generated. You can either check this into your
|
||||
project's directory or build directly from the source, as the project itself
|
||||
does.
|
||||
|
||||
Enabling Breakpad in your application requires you to `#include
|
||||
"exception_handler.h"` and instantiate the `ExceptionHandler` object like so:
|
||||
|
||||
```
|
||||
handler = new ExceptionHandler(const wstring& dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
int handler_types,
|
||||
MINIDUMP_TYPE dump_type,
|
||||
const wchar_t* pipe_name,
|
||||
const CustomClientInfo* custom_info);
|
||||
```
|
||||
|
||||
The parameters, in order, are:
|
||||
|
||||
* pathname for minidumps to be written to - this is ignored if OOP dump
|
||||
generation is used
|
||||
* A callback that is called when the exception is first handled - you can
|
||||
return true/false here to continue/stop exception processing
|
||||
* A callback that is called after minidumps have been written
|
||||
* Context for the callbacks
|
||||
* Which exceptions to handle - see `HandlerType` enumeration in
|
||||
exception\_handler.h
|
||||
* The type of minidump to generate, using the `MINIDUMP_TYPE` definitions in
|
||||
`DbgHelp.h`
|
||||
* A pipe name that can be used to communicate with a crash generation server
|
||||
* A pointer to a CustomClientInfo class that can be used to send custom data
|
||||
along with the minidump when using OOP generation
|
||||
|
||||
You can also see `src/client/windows/tests/crash_generation_app/*` for a sample
|
||||
app that uses OOP generation.
|
||||
|
||||
## OOP Minidump Generation
|
||||
|
||||
For out of process minidump generation, more work is needed. If you look inside
|
||||
`src/client/windows/crash_generation`, you will see a file called
|
||||
`crash_generation_server.h`. This file is the interface for a crash generation
|
||||
server, which must be instantiated with the same pipe name that is passed to the
|
||||
client above. The logistics of running a separate process that instantiates the
|
||||
crash generation server is left up to you, however.
|
||||
|
||||
## Build process specifics(symbol generation, upload)
|
||||
|
||||
The symbol creation step is talked about in the general overview doc, since it
|
||||
doesn't vary much by platform. You'll need to make sure that the symbols are
|
||||
available wherever minidumps are uploaded to for processing.
|
||||
|
||||
## Out in the field - uploading the minidump
|
||||
|
||||
Inside `src/client/windows/sender` is a class implementation called
|
||||
`CrashReportSender`. This class can be compiled into a separate standalone CLI
|
||||
or in the crash generation server and used to upload the report; it can know
|
||||
when to do so via one of the callbacks provided by the `CrashGenerationServer`
|
||||
or the `ExceptionHandler` object for in-process generation.
|
@ -1,558 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXXFLAGS to
|
||||
# enable support. VERSION may be '11' (for the C++11 standard) or '14'
|
||||
# (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 1
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [],
|
||||
[$1], [14], [],
|
||||
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
[$2], [noext], [],
|
||||
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++$1 -std=gnu++0x; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$2], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
AC_LANG_POP([C++])
|
||||
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||
fi
|
||||
else
|
||||
if test x$ac_success = xno; then
|
||||
HAVE_CXX$1=0
|
||||
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||
else
|
||||
HAVE_CXX$1=1
|
||||
AC_DEFINE(HAVE_CXX$1,1,
|
||||
[define if the compiler supports basic C++$1 syntax])
|
||||
fi
|
||||
|
||||
AC_SUBST(HAVE_CXX$1)
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx11
|
||||
{
|
||||
|
||||
namespace test_static_assert
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_final_override
|
||||
{
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_double_right_angle_brackets
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct check {};
|
||||
|
||||
typedef check<void> single_type;
|
||||
typedef check<check<void>> double_type;
|
||||
typedef check<check<check<void>>> triple_type;
|
||||
typedef check<check<check<check<void>>>> quadruple_type;
|
||||
|
||||
}
|
||||
|
||||
namespace test_decltype
|
||||
{
|
||||
|
||||
int
|
||||
f()
|
||||
{
|
||||
int a = 1;
|
||||
decltype(a) b = 2;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_type_deduction
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
auto
|
||||
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||
{
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
int
|
||||
test(const int c, volatile int v)
|
||||
{
|
||||
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||
auto ac = c;
|
||||
auto av = v;
|
||||
auto sumi = ac + av + 'x';
|
||||
auto sumf = ac + av + 1.0;
|
||||
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||
return (sumf > 0.0) ? sumi : add(c, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_noexcept
|
||||
{
|
||||
|
||||
int f() { return 0; }
|
||||
int g() noexcept { return 0; }
|
||||
|
||||
static_assert(noexcept(f()) == false, "");
|
||||
static_assert(noexcept(g()) == true, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||
{
|
||||
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||
}
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
return strlen_c_r(s, 0UL);
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("1") == 1UL, "");
|
||||
static_assert(strlen_c("example") == 7UL, "");
|
||||
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_rvalue_references
|
||||
{
|
||||
|
||||
template < int N >
|
||||
struct answer
|
||||
{
|
||||
static constexpr int value = N;
|
||||
};
|
||||
|
||||
answer<1> f(int&) { return answer<1>(); }
|
||||
answer<2> f(const int&) { return answer<2>(); }
|
||||
answer<3> f(int&&) { return answer<3>(); }
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 0;
|
||||
const int c = 0;
|
||||
static_assert(decltype(f(i))::value == 1, "");
|
||||
static_assert(decltype(f(c))::value == 2, "");
|
||||
static_assert(decltype(f(0))::value == 3, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_uniform_initialization
|
||||
{
|
||||
|
||||
struct test
|
||||
{
|
||||
static const int zero {};
|
||||
static const int one {1};
|
||||
};
|
||||
|
||||
static_assert(test::zero == 0, "");
|
||||
static_assert(test::one == 1, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambdas
|
||||
{
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
auto lambda1 = [](){};
|
||||
auto lambda2 = lambda1;
|
||||
lambda1();
|
||||
lambda2();
|
||||
}
|
||||
|
||||
int
|
||||
test2()
|
||||
{
|
||||
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||
auto b = []() -> int { return '0'; }();
|
||||
auto c = [=](){ return a + b; }();
|
||||
auto d = [&](){ return c; }();
|
||||
auto e = [a, &b](int x) mutable {
|
||||
const auto identity = [](int y){ return y; };
|
||||
for (auto i = 0; i < a; ++i)
|
||||
a += b--;
|
||||
return x + identity(a + b);
|
||||
}(0);
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
int
|
||||
test3()
|
||||
{
|
||||
const auto nullary = [](){ return 0; };
|
||||
const auto unary = [](int x){ return x; };
|
||||
using nullary_t = decltype(nullary);
|
||||
using unary_t = decltype(unary);
|
||||
const auto higher1st = [](nullary_t f){ return f(); };
|
||||
const auto higher2nd = [unary](nullary_t f1){
|
||||
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||
};
|
||||
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_variadic_templates
|
||||
{
|
||||
|
||||
template <int...>
|
||||
struct sum;
|
||||
|
||||
template <int N0, int... N1toN>
|
||||
struct sum<N0, N1toN...>
|
||||
{
|
||||
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct sum<>
|
||||
{
|
||||
static constexpr auto value = 0;
|
||||
};
|
||||
|
||||
static_assert(sum<>::value == 0, "");
|
||||
static_assert(sum<1>::value == 1, "");
|
||||
static_assert(sum<23>::value == 23, "");
|
||||
static_assert(sum<1, 2>::value == 3, "");
|
||||
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||
// because of this.
|
||||
namespace test_template_alias_sfinae
|
||||
{
|
||||
|
||||
struct foo {};
|
||||
|
||||
template<typename T>
|
||||
using member = typename T::member_type;
|
||||
|
||||
template<typename T>
|
||||
void func(...) {}
|
||||
|
||||
template<typename T>
|
||||
void func(member<T>*) {}
|
||||
|
||||
void test();
|
||||
|
||||
void test() { func<foo>(0); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx11
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++14
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx14
|
||||
{
|
||||
|
||||
namespace test_polymorphic_lambdas
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
const auto lambda = [](auto&&... args){
|
||||
const auto istiny = [](auto x){
|
||||
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||
};
|
||||
const int aretiny[] = { istiny(args)... };
|
||||
return aretiny[0];
|
||||
};
|
||||
return lambda(1, 1L, 1.0f, '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_binary_literals
|
||||
{
|
||||
|
||||
constexpr auto ivii = 0b0000000000101010;
|
||||
static_assert(ivii == 42, "wrong value");
|
||||
|
||||
}
|
||||
|
||||
namespace test_generalized_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
constexpr unsigned long
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
auto length = 0UL;
|
||||
for (auto p = s; *p; ++p)
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("x") == 1UL, "");
|
||||
static_assert(strlen_c("test") == 4UL, "");
|
||||
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_init_capture
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||
return lambda2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_seperators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
static_assert(ten_million == 100000000, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_return_type_deduction
|
||||
{
|
||||
|
||||
auto f(int& x) { return x; }
|
||||
decltype(auto) g(int& x) { return x; }
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx14
|
||||
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
@ -1,43 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
setup_env() {
|
||||
# Travis sets CC/CXX to the system toolchain, so our .travis.yml
|
||||
# exports USE_{CC,CXX} for this script to use.
|
||||
if [ -n "$USE_CC" ]; then
|
||||
export CC=$USE_CC
|
||||
fi
|
||||
if [ -n "$USE_CXX" ]; then
|
||||
export CXX=$USE_CXX
|
||||
fi
|
||||
# Use -jN for faster builds. Travis build machines under Docker
|
||||
# have a lot of cores, but are memory-limited, so the kernel
|
||||
# will OOM if we try to use them all, so use at most 4.
|
||||
# See https://github.com/travis-ci/travis-ci/issues/1972
|
||||
export NCPUS=$(getconf _NPROCESSORS_ONLN)
|
||||
export JOBS=$(( $NCPUS < 4 ? $NCPUS : 4 ))
|
||||
}
|
||||
|
||||
# Do an in-tree build and make sure tests pass.
|
||||
build() {
|
||||
./configure
|
||||
make -j${JOBS} check VERBOSE=1
|
||||
make distclean
|
||||
}
|
||||
|
||||
# Do an out-of-tree build and make sure we can create a release tarball.
|
||||
build_out_of_tree() {
|
||||
mkdir -p build/native
|
||||
cd build/native
|
||||
../../configure
|
||||
make -j${JOBS} distcheck VERBOSE=1
|
||||
}
|
||||
|
||||
main() {
|
||||
setup_env
|
||||
build
|
||||
build_out_of_tree
|
||||
}
|
||||
|
||||
main "$@"
|
@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
get_depot_tools() {
|
||||
cd
|
||||
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
PATH="$HOME/depot_tools:$PATH"
|
||||
}
|
||||
|
||||
gclient_sync() {
|
||||
# Rename the source dir to match what gclient expects.
|
||||
srcdir=$(basename "$TRAVIS_BUILD_DIR")
|
||||
cd "${TRAVIS_BUILD_DIR}"/..
|
||||
mv "${srcdir}" src
|
||||
gclient config --unmanaged https://github.com/google/breakpad.git
|
||||
gclient sync
|
||||
}
|
||||
|
||||
main() {
|
||||
get_depot_tools
|
||||
gclient_sync
|
||||
}
|
||||
|
||||
main "$@"
|
@ -30,28 +30,7 @@
|
||||
#ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
|
||||
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
|
||||
// the number of memory regions differs, so there is no point in running
|
||||
// this test if AddressSanitizer is used.
|
||||
//
|
||||
// Ideally we'd use this attribute to disable ASAN on a per-func basis,
|
||||
// but this doesn't seem to actually work, and it's changed names over
|
||||
// time. So just stick with disabling the actual tests.
|
||||
// http://crbug.com/304575
|
||||
//#define NO_ASAN __attribute__((no_sanitize_address))
|
||||
#if defined(__clang__) && defined(__has_feature)
|
||||
// Have to keep this check sep from above as newer gcc will barf on it.
|
||||
# if __has_feature(address_sanitizer)
|
||||
# define ADDRESS_SANITIZER
|
||||
# endif
|
||||
#elif defined(__GNUC__) && defined(__SANITIZE_ADDRESS__)
|
||||
# define ADDRESS_SANITIZER
|
||||
#else
|
||||
# undef ADDRESS_SANITIZER
|
||||
#endif
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
|
||||
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
|
@ -1,41 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'All',
|
||||
'type': 'none',
|
||||
'dependencies': [
|
||||
'../common/common.gyp:*',
|
||||
'../processor/processor.gyp:*',
|
||||
'../tools/tools.gyp:*',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'target_conditions': [
|
||||
['OS!="win"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)windows/'],
|
||||
],
|
||||
}],
|
||||
['OS!="linux"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)linux/'],
|
||||
],
|
||||
}],
|
||||
['OS!="mac"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)mac/'],
|
||||
],
|
||||
}],
|
||||
['OS!="android"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)android/'],
|
||||
],
|
||||
}],
|
||||
['OS!="solaris"', {
|
||||
'sources/': [
|
||||
['exclude', '(^|/)solaris/'],
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
breakpad_root = os.path.abspath(os.path.join(script_dir, os.pardir))
|
||||
|
||||
sys.path.insert(0, os.path.join(breakpad_root, 'tools', 'gyp', 'pylib'))
|
||||
import gyp
|
||||
|
||||
def run_gyp(args):
|
||||
rc = gyp.main(args)
|
||||
if rc != 0:
|
||||
print 'Error running GYP'
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
args.append(os.path.join(script_dir, 'all.gyp'))
|
||||
|
||||
args.append('-I')
|
||||
args.append(os.path.join(breakpad_root, 'build', 'common.gypi'))
|
||||
|
||||
args.extend(['-D', 'gyp_output_dir=out'])
|
||||
|
||||
# Set the GYP DEPTH variable to the root of the project.
|
||||
args.append('--depth=' + os.path.relpath(breakpad_root))
|
||||
|
||||
print 'Updating projects from gyp files...'
|
||||
sys.stdout.flush()
|
||||
|
||||
run_gyp(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -1,90 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'gtest',
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'../testing/gtest/src/gtest-all.cc',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../testing/gtest',
|
||||
'../testing/gtest/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../testing/gtest/include',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
'target_name': 'gtest_main',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/src/gtest_main.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'gmock',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'../testing/src/gmock-all.cc',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../testing',
|
||||
'../testing/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../testing/include',
|
||||
],
|
||||
},
|
||||
'export_dependent_settings': [
|
||||
'gtest',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'gmock_main',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'gmock',
|
||||
],
|
||||
'sources': [
|
||||
'../testing/src/gmock_main.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Keys for configuration file
|
||||
#define kReporterMinidumpDirectoryKey "MinidumpDir"
|
||||
#define kReporterMinidumpIDKey "MinidumpID"
|
||||
|
||||
// Filename for recording uploaded IDs
|
||||
#define kReporterLogFilename "uploads.log"
|
||||
|
||||
// The default subdirectory of the Library to put crash dumps in
|
||||
// The subdirectory is
|
||||
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
|
||||
#define kDefaultLibrarySubdirectory "Breakpad"
|
||||
|
||||
// Specify some special keys to be used in the configuration file that is
|
||||
// generated by Breakpad and consumed by the crash_sender.
|
||||
#define BREAKPAD_PRODUCT "BreakpadProduct"
|
||||
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
|
||||
#define BREAKPAD_VERSION "BreakpadVersion"
|
||||
#define BREAKPAD_VENDOR "BreakpadVendor"
|
||||
#define BREAKPAD_URL "BreakpadURL"
|
||||
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
|
||||
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
|
||||
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout"
|
||||
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
|
||||
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation"
|
||||
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
|
||||
#define BREAKPAD_REPORTER_EXE_LOCATION \
|
||||
"BreakpadReporterExeLocation"
|
||||
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
|
||||
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
|
||||
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
|
||||
#define BREAKPAD_COMMENTS "BreakpadComments"
|
||||
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
|
||||
#define BREAKPAD_EMAIL "BreakpadEmail"
|
||||
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
|
||||
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
|
||||
#define BREAKPAD_IN_PROCESS "BreakpadInProcess"
|
||||
|
||||
// The keys below are NOT user supplied, and are used internally.
|
||||
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"
|
||||
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime"
|
||||
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime"
|
||||
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
|
||||
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
|
||||
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
|
@ -1,246 +0,0 @@
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Framework to provide a simple C API to crash reporting for
|
||||
// applications. By default, if any machine-level exception (e.g.,
|
||||
// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
|
||||
// object as follows:
|
||||
//
|
||||
// 1. Create a minidump file (see Breakpad for details)
|
||||
// 2. Create a config file.
|
||||
//
|
||||
// These files can then be uploaded to a server.
|
||||
|
||||
typedef void *BreakpadRef;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <client/apple/Framework/BreakpadDefines.h>
|
||||
|
||||
// The keys in the dictionary returned by |BreakpadGenerateReport|.
|
||||
#define BREAKPAD_OUTPUT_DUMP_FILE "BreakpadDumpFile"
|
||||
#define BREAKPAD_OUTPUT_CONFIG_FILE "BreakpadConfigFile"
|
||||
|
||||
// Optional user-defined function to decide if we should handle this crash or
|
||||
// forward it along.
|
||||
// Return true if you want Breakpad to handle it.
|
||||
// Return false if you want Breakpad to skip it
|
||||
// The exception handler always returns false, as if SEND_AND_EXIT were false
|
||||
// (which means the next exception handler will take the exception)
|
||||
typedef bool (*BreakpadFilterCallback)(int exception_type,
|
||||
int exception_code,
|
||||
mach_port_t crashing_thread,
|
||||
void *context);
|
||||
|
||||
// Create a new BreakpadRef object and install it as an exception
|
||||
// handler. The |parameters| will typically be the contents of your
|
||||
// bundle's Info.plist.
|
||||
//
|
||||
// You can also specify these additional keys for customizable behavior:
|
||||
// Key: Value:
|
||||
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
|
||||
// This one is used as the key to identify
|
||||
// the product when uploading. Falls back to
|
||||
// CFBundleName if not specified.
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
|
||||
// name for the product when the crash_sender
|
||||
// pops up UI for the user. Falls back first to
|
||||
// CFBundleDisplayName and then to
|
||||
// BREAKPAD_PRODUCT if not specified.
|
||||
//
|
||||
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
|
||||
// as metadata for crash report. Falls back to
|
||||
// CFBundleVersion if not specified.
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
|
||||
// been created that you can send to <vendor>")
|
||||
//
|
||||
// BREAKPAD_URL URL destination for reporting
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
|
||||
// in. By default, we use
|
||||
// ~/Library/Cache/Breakpad/<BREAKPAD_PRODUCT>
|
||||
// The path you specify here is tilde-expanded.
|
||||
//
|
||||
// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
|
||||
// rewrite the upload parameters for a specific
|
||||
// server type. The currently valid values are
|
||||
// 'socorro' or 'google'. If you want to add
|
||||
// other types, see the function in
|
||||
// crash_report_sender.m that maps parameters to
|
||||
// URL parameters. Defaults to 'google'.
|
||||
//
|
||||
// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
|
||||
// parameters that are uploaded to the
|
||||
// server. The parameters are sent as
|
||||
// is to the crash server. Their
|
||||
// content isn't added to the minidump
|
||||
// but pass as URL parameters when
|
||||
// uploading theminidump to the crash
|
||||
// server.
|
||||
//=============================================================================
|
||||
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
|
||||
// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
|
||||
// will be the CFBundleName and the BREAKPAD_VERSION will be the
|
||||
// CFBundleVersion when these keys are present in the bundle's
|
||||
// Info.plist, which is usually passed in to BreakpadCreate() as an
|
||||
// NSDictionary (you could also pass in another dictionary that had
|
||||
// the same keys configured). If the BREAKPAD_PRODUCT or
|
||||
// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
|
||||
// fail. You have been warned.
|
||||
//
|
||||
// If you are running in a debugger, Breakpad will not install, unless the
|
||||
// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
|
||||
//
|
||||
//=============================================================================
|
||||
// The following are NOT user-supplied but are documented here for
|
||||
// completeness. They are calculated by Breakpad during initialization &
|
||||
// crash-dump generation, or entered in by the user.
|
||||
//
|
||||
// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
|
||||
// process started
|
||||
//
|
||||
// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
|
||||
// process crashed.
|
||||
//
|
||||
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
|
||||
// has been running. This parameter is not
|
||||
// set until the crash-dump-generation phase.
|
||||
//
|
||||
// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
|
||||
// internally, because Breakpad uses
|
||||
// the same dictionary internally to
|
||||
// track both its internal
|
||||
// configuration parameters and
|
||||
// parameters meant to be uploaded
|
||||
// to the server. This string is
|
||||
// used internally by Breakpad to
|
||||
// prefix user-supplied parameter
|
||||
// names so those can be sent to the
|
||||
// server without leaking Breakpad's
|
||||
// internal values.
|
||||
|
||||
// Returns a new BreakpadRef object on success, NULL otherwise.
|
||||
BreakpadRef BreakpadCreate(NSDictionary *parameters);
|
||||
|
||||
// Uninstall and release the data associated with |ref|.
|
||||
void BreakpadRelease(BreakpadRef ref);
|
||||
|
||||
// User defined key and value string storage. Generally this is used
|
||||
// to configure Breakpad's internal operation, such as whether the
|
||||
// crash_sender should prompt the user, or the filesystem location for
|
||||
// the minidump file. See Breakpad.h for some parameters that can be
|
||||
// set. Anything longer than 255 bytes will be truncated. Note that
|
||||
// the string is converted to UTF8 before truncation, so any multibyte
|
||||
// character that straddles the 255(256 - 1 for terminator) byte limit
|
||||
// will be mangled.
|
||||
//
|
||||
// A maximum number of 64 key/value pairs are supported. An assert()
|
||||
// will fire if more than this number are set. Unfortunately, right
|
||||
// now, the same dictionary is used for both Breakpad's parameters AND
|
||||
// the Upload parameters.
|
||||
//
|
||||
// TODO (nealsid): Investigate how necessary this is if we don't
|
||||
// automatically upload parameters to the server anymore.
|
||||
// TODO (nealsid): separate server parameter dictionary from the
|
||||
// dictionary used to configure Breakpad, and document limits for each
|
||||
// independently.
|
||||
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
|
||||
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
|
||||
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
|
||||
|
||||
// You can use this method to specify parameters that will be uploaded
|
||||
// to the crash server. They will be automatically encoded as
|
||||
// necessary. Note that as mentioned above there are limits on both
|
||||
// the number of keys and their length.
|
||||
void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
|
||||
NSString *value);
|
||||
|
||||
// This method will remove a previously-added parameter from the
|
||||
// upload parameter set.
|
||||
void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
|
||||
|
||||
// Method to handle uploading data to the server
|
||||
|
||||
// Returns the number of crash reports waiting to send to the server.
|
||||
int BreakpadGetCrashReportCount(BreakpadRef ref);
|
||||
|
||||
// Returns the next upload configuration. The report file is deleted.
|
||||
NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref);
|
||||
|
||||
// Upload next report to the server.
|
||||
void BreakpadUploadNextReport(BreakpadRef ref);
|
||||
|
||||
// Upload next report to the server.
|
||||
// |server_parameters| is additional server parameters to send.
|
||||
void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
|
||||
NSDictionary *server_parameters);
|
||||
|
||||
// Upload a report to the server.
|
||||
// |server_parameters| is additional server parameters to send.
|
||||
// |configuration| is the configuration of the breakpad report to send.
|
||||
void BreakpadUploadReportWithParametersAndConfiguration(
|
||||
BreakpadRef ref,
|
||||
NSDictionary *server_parameters,
|
||||
NSDictionary *configuration);
|
||||
|
||||
// Handles the network response of a breakpad upload. This function is needed if
|
||||
// the actual upload is done by the Breakpad client.
|
||||
// |configuration| is the configuration of the upload. It must contain the same
|
||||
// fields as the configuration passed to
|
||||
// BreakpadUploadReportWithParametersAndConfiguration.
|
||||
// |data| and |error| contain the network response.
|
||||
void BreakpadHandleNetworkResponse(BreakpadRef ref,
|
||||
NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error);
|
||||
|
||||
// Upload a file to the server. |data| is the content of the file to sent.
|
||||
// |server_parameters| is additional server parameters to send.
|
||||
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters);
|
||||
|
||||
// Generate a breakpad minidump and configuration file in the dump directory.
|
||||
// The report will be available for uploading. The paths of the created files
|
||||
// are returned in the dictionary. |server_parameters| is additional server
|
||||
// parameters to add in the config file.
|
||||
NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
|
||||
NSDictionary *server_parameters);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,916 +0,0 @@
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
||||
|
||||
#import "client/ios/Breakpad.h"
|
||||
|
||||
#include <assert.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#import "client/ios/handler/ios_exception_minidump_generator.h"
|
||||
#import "client/mac/crash_generation/ConfigFile.h"
|
||||
#import "client/mac/handler/exception_handler.h"
|
||||
#import "client/mac/handler/minidump_generator.h"
|
||||
#import "client/mac/sender/uploader.h"
|
||||
#import "client/mac/handler/protected_memory_allocator.h"
|
||||
#import "common/simple_string_dictionary.h"
|
||||
|
||||
#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
|
||||
// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
|
||||
// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
|
||||
// exceptions disabled even when other C++ libraries are used. #undef the try
|
||||
// and catch macros first in case libstdc++ is in use and has already provided
|
||||
// its own definitions.
|
||||
#undef try
|
||||
#define try if (true)
|
||||
#undef catch
|
||||
#define catch(X) if (false)
|
||||
#endif // __EXCEPTIONS
|
||||
|
||||
using google_breakpad::ConfigFile;
|
||||
using google_breakpad::EnsureDirectoryPathExists;
|
||||
using google_breakpad::SimpleStringDictionary;
|
||||
|
||||
//=============================================================================
|
||||
// We want any memory allocations which are used by breakpad during the
|
||||
// exception handling process (after a crash has happened) to be read-only
|
||||
// to prevent them from being smashed before a crash occurs. Unfortunately
|
||||
// we cannot protect against smashes to our exception handling thread's
|
||||
// stack.
|
||||
//
|
||||
// NOTE: Any memory allocations which are not used during the exception
|
||||
// handling process may be allocated in the normal ways.
|
||||
//
|
||||
// The ProtectedMemoryAllocator class provides an Allocate() method which
|
||||
// we'll using in conjunction with placement operator new() to control
|
||||
// allocation of C++ objects. Note that we don't use operator delete()
|
||||
// but instead call the objects destructor directly: object->~ClassName();
|
||||
//
|
||||
ProtectedMemoryAllocator *gMasterAllocator = NULL;
|
||||
ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
|
||||
ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
|
||||
|
||||
// Mutex for thread-safe access to the key/value dictionary used by breakpad.
|
||||
// It's a global instead of an instance variable of Breakpad
|
||||
// since it can't live in a protected memory area.
|
||||
pthread_mutex_t gDictionaryMutex;
|
||||
|
||||
//=============================================================================
|
||||
// Stack-based object for thread-safe access to a memory-protected region.
|
||||
// It's assumed that normally the memory block (allocated by the allocator)
|
||||
// is protected (read-only). Creating a stack-based instance of
|
||||
// ProtectedMemoryLocker will unprotect this block after taking the lock.
|
||||
// Its destructor will first re-protect the memory then release the lock.
|
||||
class ProtectedMemoryLocker {
|
||||
public:
|
||||
ProtectedMemoryLocker(pthread_mutex_t *mutex,
|
||||
ProtectedMemoryAllocator *allocator)
|
||||
: mutex_(mutex),
|
||||
allocator_(allocator) {
|
||||
// Lock the mutex
|
||||
__attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
|
||||
assert(rv == 0);
|
||||
|
||||
// Unprotect the memory
|
||||
allocator_->Unprotect();
|
||||
}
|
||||
|
||||
~ProtectedMemoryLocker() {
|
||||
// First protect the memory
|
||||
allocator_->Protect();
|
||||
|
||||
// Then unlock the mutex
|
||||
__attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
|
||||
assert(rv == 0);
|
||||
};
|
||||
|
||||
private:
|
||||
ProtectedMemoryLocker();
|
||||
ProtectedMemoryLocker(const ProtectedMemoryLocker&);
|
||||
ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
|
||||
|
||||
pthread_mutex_t *mutex_;
|
||||
ProtectedMemoryAllocator *allocator_;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
class Breakpad {
|
||||
public:
|
||||
// factory method
|
||||
static Breakpad *Create(NSDictionary *parameters) {
|
||||
// Allocate from our special allocation pool
|
||||
Breakpad *breakpad =
|
||||
new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
|
||||
Breakpad();
|
||||
|
||||
if (!breakpad)
|
||||
return NULL;
|
||||
|
||||
if (!breakpad->Initialize(parameters)) {
|
||||
// Don't use operator delete() here since we allocated from special pool
|
||||
breakpad->~Breakpad();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return breakpad;
|
||||
}
|
||||
|
||||
~Breakpad();
|
||||
|
||||
void SetKeyValue(NSString *key, NSString *value);
|
||||
NSString *KeyValue(NSString *key);
|
||||
void RemoveKeyValue(NSString *key);
|
||||
NSArray *CrashReportsToUpload();
|
||||
NSString *NextCrashReportToUpload();
|
||||
NSDictionary *NextCrashReportConfiguration();
|
||||
void UploadNextReport(NSDictionary *server_parameters);
|
||||
void UploadReportWithConfiguration(NSDictionary *configuration,
|
||||
NSDictionary *server_parameters);
|
||||
void UploadData(NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters);
|
||||
void HandleNetworkResponse(NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error);
|
||||
NSDictionary *GenerateReport(NSDictionary *server_parameters);
|
||||
|
||||
private:
|
||||
Breakpad()
|
||||
: handler_(NULL),
|
||||
config_params_(NULL) {}
|
||||
|
||||
bool Initialize(NSDictionary *parameters);
|
||||
|
||||
bool ExtractParameters(NSDictionary *parameters);
|
||||
|
||||
// Dispatches to HandleMinidump()
|
||||
static bool HandleMinidumpCallback(const char *dump_dir,
|
||||
const char *minidump_id,
|
||||
void *context, bool succeeded);
|
||||
|
||||
bool HandleMinidump(const char *dump_dir,
|
||||
const char *minidump_id);
|
||||
|
||||
// NSException handler
|
||||
static void UncaughtExceptionHandler(NSException *exception);
|
||||
|
||||
// Handle an uncaught NSException.
|
||||
void HandleUncaughtException(NSException *exception);
|
||||
|
||||
// Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
|
||||
// MachineExceptions.h, we have to explicitly name the handler.
|
||||
google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
|
||||
|
||||
SimpleStringDictionary *config_params_; // Create parameters (STRONG)
|
||||
|
||||
ConfigFile config_file_;
|
||||
|
||||
// A static reference to the current Breakpad instance. Used for handling
|
||||
// NSException.
|
||||
static Breakpad *current_breakpad_;
|
||||
};
|
||||
|
||||
Breakpad *Breakpad::current_breakpad_ = NULL;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Helper functions
|
||||
|
||||
//=============================================================================
|
||||
// Helper functions
|
||||
|
||||
//=============================================================================
|
||||
static BOOL IsDebuggerActive() {
|
||||
BOOL result = NO;
|
||||
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
// We check both defaults and the environment variable here
|
||||
|
||||
BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
|
||||
|
||||
if (!ignoreDebugger) {
|
||||
char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
|
||||
ignoreDebugger =
|
||||
(ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
|
||||
}
|
||||
|
||||
if (!ignoreDebugger) {
|
||||
pid_t pid = getpid();
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
||||
int mibSize = sizeof(mib) / sizeof(int);
|
||||
size_t actualSize;
|
||||
|
||||
if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
|
||||
struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
|
||||
|
||||
if (info) {
|
||||
// This comes from looking at the Darwin xnu Kernel
|
||||
if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
|
||||
result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
|
||||
|
||||
free(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
|
||||
const char *minidump_id,
|
||||
void *context, bool succeeded) {
|
||||
Breakpad *breakpad = (Breakpad *)context;
|
||||
|
||||
// If our context is damaged or something, just return false to indicate that
|
||||
// the handler should continue without us.
|
||||
if (!breakpad || !succeeded)
|
||||
return false;
|
||||
|
||||
return breakpad->HandleMinidump(dump_dir, minidump_id);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UncaughtExceptionHandler(NSException *exception) {
|
||||
NSSetUncaughtExceptionHandler(NULL);
|
||||
if (current_breakpad_) {
|
||||
current_breakpad_->HandleUncaughtException(exception);
|
||||
BreakpadRelease(current_breakpad_);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
#pragma mark -
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::Initialize(NSDictionary *parameters) {
|
||||
// Initialize
|
||||
current_breakpad_ = this;
|
||||
config_params_ = NULL;
|
||||
handler_ = NULL;
|
||||
|
||||
// Gather any user specified parameters
|
||||
if (!ExtractParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for debugger
|
||||
if (IsDebuggerActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create the handler (allocating it in our special protected pool)
|
||||
handler_ =
|
||||
new (gBreakpadAllocator->Allocate(
|
||||
sizeof(google_breakpad::ExceptionHandler)))
|
||||
google_breakpad::ExceptionHandler(
|
||||
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
|
||||
0, &HandleMinidumpCallback, this, true, 0);
|
||||
NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
Breakpad::~Breakpad() {
|
||||
NSSetUncaughtExceptionHandler(NULL);
|
||||
current_breakpad_ = NULL;
|
||||
// Note that we don't use operator delete() on these pointers,
|
||||
// since they were allocated by ProtectedMemoryAllocator objects.
|
||||
//
|
||||
if (config_params_) {
|
||||
config_params_->~SimpleStringDictionary();
|
||||
}
|
||||
|
||||
if (handler_)
|
||||
handler_->~ExceptionHandler();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
||||
NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
|
||||
NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||
NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
|
||||
NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
|
||||
NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
|
||||
NSString *vendor =
|
||||
[parameters objectForKey:@BREAKPAD_VENDOR];
|
||||
// We check both parameters and the environment variable here.
|
||||
char *envVarDumpSubdirectory = getenv(BREAKPAD_DUMP_DIRECTORY);
|
||||
NSString *dumpSubdirectory = envVarDumpSubdirectory ?
|
||||
[NSString stringWithUTF8String:envVarDumpSubdirectory] :
|
||||
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
|
||||
|
||||
NSDictionary *serverParameters =
|
||||
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
|
||||
|
||||
if (!product)
|
||||
product = [parameters objectForKey:@"CFBundleName"];
|
||||
|
||||
if (!display) {
|
||||
display = [parameters objectForKey:@"CFBundleDisplayName"];
|
||||
if (!display) {
|
||||
display = product;
|
||||
}
|
||||
}
|
||||
|
||||
if (!version.length) // Default nil or empty string to CFBundleVersion
|
||||
version = [parameters objectForKey:@"CFBundleVersion"];
|
||||
|
||||
if (!vendor) {
|
||||
vendor = @"Vendor not specified";
|
||||
}
|
||||
|
||||
if (!dumpSubdirectory) {
|
||||
NSString *cachePath =
|
||||
[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
|
||||
NSUserDomainMask,
|
||||
YES)
|
||||
objectAtIndex:0];
|
||||
dumpSubdirectory =
|
||||
[cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
|
||||
|
||||
EnsureDirectoryPathExists(dumpSubdirectory);
|
||||
}
|
||||
|
||||
// The product, version, and URL are required values.
|
||||
if (![product length]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (![version length]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (![urlStr length]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config_params_ =
|
||||
new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
|
||||
SimpleStringDictionary();
|
||||
|
||||
SimpleStringDictionary &dictionary = *config_params_;
|
||||
|
||||
dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
|
||||
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
|
||||
[dumpSubdirectory UTF8String]);
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
char timeStartedString[32];
|
||||
sprintf(timeStartedString, "%zd", tv.tv_sec);
|
||||
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
|
||||
|
||||
if (serverParameters) {
|
||||
// For each key-value pair, call BreakpadAddUploadParameter()
|
||||
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
|
||||
NSString *aParameter;
|
||||
while ((aParameter = [keyEnumerator nextObject])) {
|
||||
BreakpadAddUploadParameter(this, aParameter,
|
||||
[serverParameters objectForKey:aParameter]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
|
||||
// We allow nil values. This is the same as removing the keyvalue.
|
||||
if (!config_params_ || !key)
|
||||
return;
|
||||
|
||||
config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSString *Breakpad::KeyValue(NSString *key) {
|
||||
if (!config_params_ || !key)
|
||||
return nil;
|
||||
|
||||
const char *value = config_params_->GetValueForKey([key UTF8String]);
|
||||
return value ? [NSString stringWithUTF8String:value] : nil;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::RemoveKeyValue(NSString *key) {
|
||||
if (!config_params_ || !key) return;
|
||||
|
||||
config_params_->RemoveKey([key UTF8String]);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSArray *Breakpad::CrashReportsToUpload() {
|
||||
NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||
if (!directory)
|
||||
return nil;
|
||||
NSArray *dirContents = [[NSFileManager defaultManager]
|
||||
contentsOfDirectoryAtPath:directory error:nil];
|
||||
NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
|
||||
predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
|
||||
return configs;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSString *Breakpad::NextCrashReportToUpload() {
|
||||
NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||
if (!directory)
|
||||
return nil;
|
||||
NSString *config = [CrashReportsToUpload() lastObject];
|
||||
if (!config)
|
||||
return nil;
|
||||
return [NSString stringWithFormat:@"%@/%@", directory, config];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *Breakpad::NextCrashReportConfiguration() {
|
||||
return [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::HandleNetworkResponse(NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
Uploader *uploader = [[[Uploader alloc]
|
||||
initWithConfig:configuration] autorelease];
|
||||
[uploader handleNetworkResponse:data withError:error];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
|
||||
NSDictionary *server_parameters) {
|
||||
Uploader *uploader = [[[Uploader alloc]
|
||||
initWithConfig:configuration] autorelease];
|
||||
if (!uploader)
|
||||
return;
|
||||
for (NSString *key in server_parameters) {
|
||||
[uploader addServerParameter:[server_parameters objectForKey:key]
|
||||
forKey:key];
|
||||
}
|
||||
[uploader report];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
|
||||
NSDictionary *configuration = NextCrashReportConfiguration();
|
||||
if (configuration) {
|
||||
return UploadReportWithConfiguration(configuration, server_parameters);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::UploadData(NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters) {
|
||||
NSMutableDictionary *config = [NSMutableDictionary dictionary];
|
||||
|
||||
SimpleStringDictionary::Iterator it(*config_params_);
|
||||
while (const SimpleStringDictionary::Entry *next = it.Next()) {
|
||||
[config setValue:[NSString stringWithUTF8String:next->value]
|
||||
forKey:[NSString stringWithUTF8String:next->key]];
|
||||
}
|
||||
|
||||
Uploader *uploader =
|
||||
[[[Uploader alloc] initWithConfig:config] autorelease];
|
||||
for (NSString *key in server_parameters) {
|
||||
[uploader addServerParameter:[server_parameters objectForKey:key]
|
||||
forKey:key];
|
||||
}
|
||||
[uploader uploadData:data name:name];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
|
||||
NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||
if (!dumpDirAsNSString)
|
||||
return nil;
|
||||
const char *dumpDir = [dumpDirAsNSString UTF8String];
|
||||
|
||||
google_breakpad::MinidumpGenerator generator(mach_task_self(),
|
||||
MACH_PORT_NULL);
|
||||
std::string dumpId;
|
||||
std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
|
||||
bool success = generator.Write(dumpFilename.c_str());
|
||||
if (!success)
|
||||
return nil;
|
||||
|
||||
SimpleStringDictionary params = *config_params_;
|
||||
for (NSString *key in server_parameters) {
|
||||
params.SetKeyValue([key UTF8String],
|
||||
[[server_parameters objectForKey:key] UTF8String]);
|
||||
}
|
||||
ConfigFile config_file;
|
||||
config_file.WriteFile(dumpDir, ¶ms, dumpDir, dumpId.c_str());
|
||||
|
||||
// Handle results.
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
||||
NSString *dumpFullPath = [NSString stringWithUTF8String:dumpFilename.c_str()];
|
||||
[result setValue:dumpFullPath
|
||||
forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
|
||||
[result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
|
||||
forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool Breakpad::HandleMinidump(const char *dump_dir,
|
||||
const char *minidump_id) {
|
||||
config_file_.WriteFile(dump_dir,
|
||||
config_params_,
|
||||
dump_dir,
|
||||
minidump_id);
|
||||
|
||||
// Return true here to indicate that we've processed things as much as we
|
||||
// want.
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Breakpad::HandleUncaughtException(NSException *exception) {
|
||||
// Generate the minidump.
|
||||
google_breakpad::IosExceptionMinidumpGenerator generator(exception);
|
||||
const char *minidump_path =
|
||||
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
|
||||
std::string minidump_id;
|
||||
std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
|
||||
&minidump_id);
|
||||
generator.Write(minidump_filename.c_str());
|
||||
|
||||
// Copy the config params and our custom parameter. This is necessary for 2
|
||||
// reasons:
|
||||
// 1- config_params_ is protected.
|
||||
// 2- If the application crash while trying to handle this exception, a usual
|
||||
// report will be generated. This report must not contain these special
|
||||
// keys.
|
||||
SimpleStringDictionary params = *config_params_;
|
||||
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
|
||||
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
|
||||
[[exception name] UTF8String]);
|
||||
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
|
||||
[[exception reason] UTF8String]);
|
||||
|
||||
// And finally write the config file.
|
||||
ConfigFile config_file;
|
||||
config_file.WriteFile(minidump_path,
|
||||
¶ms,
|
||||
minidump_path,
|
||||
minidump_id.c_str());
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Public API
|
||||
|
||||
//=============================================================================
|
||||
BreakpadRef BreakpadCreate(NSDictionary *parameters) {
|
||||
try {
|
||||
// This is confusing. Our two main allocators for breakpad memory are:
|
||||
// - gKeyValueAllocator for the key/value memory
|
||||
// - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
|
||||
// breakpad allocations which are accessed at exception handling time.
|
||||
//
|
||||
// But in order to avoid these two allocators themselves from being smashed,
|
||||
// we'll protect them as well by allocating them with gMasterAllocator.
|
||||
//
|
||||
// gMasterAllocator itself will NOT be protected, but this doesn't matter,
|
||||
// since once it does its allocations and locks the memory, smashes to
|
||||
// itself don't affect anything we care about.
|
||||
gMasterAllocator =
|
||||
new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
|
||||
|
||||
gKeyValueAllocator =
|
||||
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
|
||||
ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
|
||||
|
||||
// Create a mutex for use in accessing the SimpleStringDictionary
|
||||
int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
|
||||
if (mutexResult == 0) {
|
||||
|
||||
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
|
||||
// Let's round up to the nearest page size.
|
||||
//
|
||||
int breakpad_pool_size = 4096;
|
||||
|
||||
/*
|
||||
sizeof(Breakpad)
|
||||
+ sizeof(google_breakpad::ExceptionHandler)
|
||||
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
|
||||
*/
|
||||
|
||||
gBreakpadAllocator =
|
||||
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
|
||||
ProtectedMemoryAllocator(breakpad_pool_size);
|
||||
|
||||
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
Breakpad *breakpad = Breakpad::Create(parameters);
|
||||
|
||||
if (breakpad) {
|
||||
// Make read-only to protect against memory smashers
|
||||
gMasterAllocator->Protect();
|
||||
gKeyValueAllocator->Protect();
|
||||
gBreakpadAllocator->Protect();
|
||||
// Can uncomment this line to figure out how much space was actually
|
||||
// allocated using this allocator
|
||||
// printf("gBreakpadAllocator allocated size = %d\n",
|
||||
// gBreakpadAllocator->GetAllocatedSize() );
|
||||
[pool release];
|
||||
return (BreakpadRef)breakpad;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadCreate() : error\n");
|
||||
}
|
||||
|
||||
if (gKeyValueAllocator) {
|
||||
gKeyValueAllocator->~ProtectedMemoryAllocator();
|
||||
gKeyValueAllocator = NULL;
|
||||
}
|
||||
|
||||
if (gBreakpadAllocator) {
|
||||
gBreakpadAllocator->~ProtectedMemoryAllocator();
|
||||
gBreakpadAllocator = NULL;
|
||||
}
|
||||
|
||||
delete gMasterAllocator;
|
||||
gMasterAllocator = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadRelease(BreakpadRef ref) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (gMasterAllocator) {
|
||||
gMasterAllocator->Unprotect();
|
||||
gKeyValueAllocator->Unprotect();
|
||||
gBreakpadAllocator->Unprotect();
|
||||
|
||||
breakpad->~Breakpad();
|
||||
|
||||
// Unfortunately, it's not possible to deallocate this stuff
|
||||
// because the exception handling thread is still finishing up
|
||||
// asynchronously at this point... OK, it could be done with
|
||||
// locks, etc. But since BreakpadRelease() should usually only
|
||||
// be called right before the process exits, it's not worth
|
||||
// deallocating this stuff.
|
||||
#if 0
|
||||
gKeyValueAllocator->~ProtectedMemoryAllocator();
|
||||
gBreakpadAllocator->~ProtectedMemoryAllocator();
|
||||
delete gMasterAllocator;
|
||||
|
||||
gMasterAllocator = NULL;
|
||||
gKeyValueAllocator = NULL;
|
||||
gBreakpadAllocator = NULL;
|
||||
#endif
|
||||
|
||||
pthread_mutex_destroy(&gDictionaryMutex);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadRelease() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
breakpad->SetKeyValue(key, value);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpadAddUploadParameter(BreakpadRef ref,
|
||||
NSString *key,
|
||||
NSString *value) {
|
||||
// The only difference, internally, between an upload parameter and
|
||||
// a key value one that is set with BreakpadSetKeyValue is that we
|
||||
// prepend the keyname with a special prefix. This informs the
|
||||
// crash sender that the parameter should be sent along with the
|
||||
// POST of the crash dump upload.
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
|
||||
stringByAppendingString:key];
|
||||
breakpad->SetKeyValue(prefixedKey, value);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpadRemoveUploadParameter(BreakpadRef ref,
|
||||
NSString *key) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
|
||||
@BREAKPAD_SERVER_PARAMETER_PREFIX, key];
|
||||
breakpad->RemoveKeyValue(prefixedKey);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
//=============================================================================
|
||||
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
|
||||
NSString *value = nil;
|
||||
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (!breakpad || !key || !gKeyValueAllocator)
|
||||
return nil;
|
||||
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
value = breakpad->KeyValue(key);
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadKeyValue() : error\n");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad && key && gKeyValueAllocator) {
|
||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||
|
||||
breakpad->RemoveKeyValue(key);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int BreakpadGetCrashReportCount(BreakpadRef ref) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad) {
|
||||
return static_cast<int>([breakpad->CrashReportsToUpload() count]);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadNextReport(BreakpadRef ref) {
|
||||
BreakpadUploadNextReportWithParameters(ref, nil);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (breakpad)
|
||||
return breakpad->NextCrashReportConfiguration();
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadGetNextReportConfiguration() : error\n");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadReportWithParametersAndConfiguration(
|
||||
BreakpadRef ref,
|
||||
NSDictionary *server_parameters,
|
||||
NSDictionary *configuration) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (!breakpad || !configuration)
|
||||
return;
|
||||
breakpad->UploadReportWithConfiguration(configuration, server_parameters);
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr,
|
||||
"BreakpadUploadReportWithParametersAndConfiguration() : error\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
|
||||
NSDictionary *server_parameters) {
|
||||
try {
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (!breakpad)
|
||||
return;
|
||||
NSDictionary *configuration = breakpad->NextCrashReportConfiguration();
|
||||
if (!configuration)
|
||||
return;
|
||||
return BreakpadUploadReportWithParametersAndConfiguration(ref,
|
||||
server_parameters,
|
||||
configuration);
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpadHandleNetworkResponse(BreakpadRef ref,
|
||||
NSDictionary *configuration,
|
||||
NSData *data,
|
||||
NSError *error) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
if (breakpad && configuration)
|
||||
breakpad->HandleNetworkResponse(configuration,data, error);
|
||||
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadHandleNetworkResponse() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
|
||||
NSDictionary *server_parameters) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad) {
|
||||
breakpad->UploadData(data, name, server_parameters);
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadUploadData() : error\n");
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
|
||||
NSDictionary *server_parameters) {
|
||||
try {
|
||||
// Not called at exception time
|
||||
Breakpad *breakpad = (Breakpad *)ref;
|
||||
|
||||
if (breakpad) {
|
||||
return breakpad->GenerateReport(server_parameters);
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} catch(...) { // don't let exceptions leave this C API
|
||||
fprintf(stderr, "BreakpadGenerateReport() : error\n");
|
||||
return nil;
|
||||
}
|
||||
}
|
@ -1,578 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
14569321182CE29F0029C465 /* ucontext_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569320182CE29F0029C465 /* ucontext_compat.h */; };
|
||||
14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569322182CE2C10029C465 /* mach_vm_compat.h */; };
|
||||
16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */; };
|
||||
16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */; };
|
||||
16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C968147D4A4200776EAD /* BreakpadDefines.h */; };
|
||||
16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C96A147D4A4200776EAD /* Breakpad.h */; };
|
||||
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7C96B147D4A4200776EAD /* Breakpad.mm */; };
|
||||
16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CB9E147D4A4300776EAD /* ConfigFile.h */; };
|
||||
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */; };
|
||||
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */; };
|
||||
16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */; };
|
||||
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */; };
|
||||
16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB0147D4A4300776EAD /* dynamic_images.h */; };
|
||||
16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB1147D4A4300776EAD /* exception_handler.cc */; };
|
||||
16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB2147D4A4300776EAD /* exception_handler.h */; };
|
||||
16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */; };
|
||||
16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB5147D4A4300776EAD /* minidump_generator.h */; };
|
||||
16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */; };
|
||||
16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */; };
|
||||
16C7CE08147D4A4300776EAD /* uploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBEA147D4A4300776EAD /* uploader.h */; };
|
||||
16C7CE09147D4A4300776EAD /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBEB147D4A4300776EAD /* uploader.mm */; };
|
||||
16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */; };
|
||||
16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */; };
|
||||
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */; };
|
||||
16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC4A147D4A4300776EAD /* convert_UTF.c */; };
|
||||
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC4B147D4A4300776EAD /* convert_UTF.h */; };
|
||||
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC88147D4A4300776EAD /* GTMLogger.h */; };
|
||||
16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC89147D4A4300776EAD /* GTMLogger.m */; };
|
||||
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */; };
|
||||
16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */; };
|
||||
16C7CE83147D4A4300776EAD /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC93147D4A4300776EAD /* file_id.cc */; };
|
||||
16C7CE84147D4A4300776EAD /* file_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC94147D4A4300776EAD /* file_id.h */; };
|
||||
16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC95147D4A4300776EAD /* macho_id.cc */; };
|
||||
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC96147D4A4300776EAD /* macho_id.h */; };
|
||||
16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */; };
|
||||
16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9B147D4A4300776EAD /* macho_utilities.h */; };
|
||||
16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9C147D4A4300776EAD /* macho_walker.cc */; };
|
||||
16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9D147D4A4300776EAD /* macho_walker.h */; };
|
||||
16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9F147D4A4300776EAD /* string_utilities.cc */; };
|
||||
16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA0147D4A4300776EAD /* string_utilities.h */; };
|
||||
16C7CE93147D4A4300776EAD /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCA4147D4A4300776EAD /* md5.cc */; };
|
||||
16C7CE94147D4A4300776EAD /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA5147D4A4300776EAD /* md5.h */; };
|
||||
16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCB9147D4A4300776EAD /* string_conversion.cc */; };
|
||||
16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCBA147D4A4300776EAD /* string_conversion.h */; };
|
||||
16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C92FAB150DF8330053D7BA /* BreakpadController.h */; };
|
||||
16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C92FAC150DF8330053D7BA /* BreakpadController.mm */; };
|
||||
1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */; };
|
||||
1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */; };
|
||||
AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */; };
|
||||
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
14569320182CE29F0029C465 /* ucontext_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ucontext_compat.h; sourceTree = "<group>"; };
|
||||
14569322182CE2C10029C465 /* mach_vm_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_vm_compat.h; sourceTree = "<group>"; };
|
||||
16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios_exception_minidump_generator.h; sourceTree = "<group>"; };
|
||||
16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios_exception_minidump_generator.mm; sourceTree = "<group>"; };
|
||||
16C7C968147D4A4200776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = "<group>"; };
|
||||
16C7C96A147D4A4200776EAD /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad.h; sourceTree = "<group>"; };
|
||||
16C7C96B147D4A4200776EAD /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Breakpad.mm; sourceTree = "<group>"; };
|
||||
16C7CB9E147D4A4300776EAD /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = "<group>"; };
|
||||
16C7CB9F147D4A4300776EAD /* ConfigFile.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConfigFile.mm; sourceTree = "<group>"; };
|
||||
16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
|
||||
16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
|
||||
16C7CBAF147D4A4300776EAD /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
|
||||
16C7CBB0147D4A4300776EAD /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
|
||||
16C7CBB1147D4A4300776EAD /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = "<group>"; };
|
||||
16C7CBB2147D4A4300776EAD /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = "<group>"; };
|
||||
16C7CBB4147D4A4300776EAD /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = "<group>"; };
|
||||
16C7CBB5147D4A4300776EAD /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = "<group>"; };
|
||||
16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protected_memory_allocator.cc; sourceTree = "<group>"; };
|
||||
16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protected_memory_allocator.h; sourceTree = "<group>"; };
|
||||
16C7CBEA147D4A4300776EAD /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = "<group>"; };
|
||||
16C7CBEB147D4A4300776EAD /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = uploader.mm; sourceTree = "<group>"; };
|
||||
16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "minidump_file_writer-inl.h"; sourceTree = "<group>"; };
|
||||
16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer.cc; sourceTree = "<group>"; };
|
||||
16C7CC06147D4A4300776EAD /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_file_writer.h; sourceTree = "<group>"; };
|
||||
16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
|
||||
16C7CC4A147D4A4300776EAD /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = convert_UTF.c; sourceTree = "<group>"; };
|
||||
16C7CC4B147D4A4300776EAD /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert_UTF.h; sourceTree = "<group>"; };
|
||||
16C7CC88147D4A4300776EAD /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = "<group>"; };
|
||||
16C7CC89147D4A4300776EAD /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = "<group>"; };
|
||||
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPMultipartUpload.h; sourceTree = "<group>"; };
|
||||
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPMultipartUpload.m; sourceTree = "<group>"; };
|
||||
16C7CC93147D4A4300776EAD /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_id.cc; sourceTree = "<group>"; };
|
||||
16C7CC94147D4A4300776EAD /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_id.h; sourceTree = "<group>"; };
|
||||
16C7CC95147D4A4300776EAD /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_id.cc; sourceTree = "<group>"; };
|
||||
16C7CC96147D4A4300776EAD /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_id.h; sourceTree = "<group>"; };
|
||||
16C7CC9A147D4A4300776EAD /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_utilities.cc; sourceTree = "<group>"; };
|
||||
16C7CC9B147D4A4300776EAD /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_utilities.h; sourceTree = "<group>"; };
|
||||
16C7CC9C147D4A4300776EAD /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_walker.cc; sourceTree = "<group>"; };
|
||||
16C7CC9D147D4A4300776EAD /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_walker.h; sourceTree = "<group>"; };
|
||||
16C7CC9F147D4A4300776EAD /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_utilities.cc; sourceTree = "<group>"; };
|
||||
16C7CCA0147D4A4300776EAD /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utilities.h; sourceTree = "<group>"; };
|
||||
16C7CCA4147D4A4300776EAD /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = "<group>"; };
|
||||
16C7CCA5147D4A4300776EAD /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
|
||||
16C7CCB9147D4A4300776EAD /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_conversion.cc; sourceTree = "<group>"; };
|
||||
16C7CCBA147D4A4300776EAD /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_conversion.h; sourceTree = "<group>"; };
|
||||
16C92FAB150DF8330053D7BA /* BreakpadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadController.h; sourceTree = "<group>"; };
|
||||
16C92FAC150DF8330053D7BA /* BreakpadController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BreakpadController.mm; sourceTree = "<group>"; };
|
||||
1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simple_string_dictionary.cc; sourceTree = "<group>"; };
|
||||
1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_string_dictionary.h; sourceTree = "<group>"; };
|
||||
AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad_Prefix.pch; sourceTree = SOURCE_ROOT; };
|
||||
AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D2AAC07C0554694100DB518D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
034768DFFF38A50411DB9C8B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2AAC07E0554694100DB518D /* libBreakpad.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0867D691FE84028FC02AAC07 /* Breakpad */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB77AEFE84172EC02AAC07 /* Classes */,
|
||||
32C88DFF0371C24200C91783 /* Other Sources */,
|
||||
0867D69AFE84028FC02AAC07 /* Frameworks */,
|
||||
034768DFFF38A50411DB9C8B /* Products */,
|
||||
);
|
||||
name = Breakpad;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0867D69AFE84028FC02AAC07 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AACBBE490F95108600F1A2B1 /* Foundation.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB77AEFE84172EC02AAC07 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C965147D4A4200776EAD /* client */,
|
||||
16C7CC47147D4A4300776EAD /* common */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16BFA66A14E195E9009704F8 /* handler */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */,
|
||||
16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */,
|
||||
);
|
||||
path = handler;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C965147D4A4200776EAD /* client */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C966147D4A4200776EAD /* apple */,
|
||||
16C7C969147D4A4200776EAD /* ios */,
|
||||
16C7C99E147D4A4200776EAD /* mac */,
|
||||
16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */,
|
||||
16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */,
|
||||
16C7CC06147D4A4300776EAD /* minidump_file_writer.h */,
|
||||
16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */,
|
||||
);
|
||||
name = client;
|
||||
path = ..;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
16C7C966147D4A4200776EAD /* apple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C967147D4A4200776EAD /* Framework */,
|
||||
);
|
||||
path = apple;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C967147D4A4200776EAD /* Framework */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C968147D4A4200776EAD /* BreakpadDefines.h */,
|
||||
);
|
||||
path = Framework;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C969147D4A4200776EAD /* ios */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C92FAB150DF8330053D7BA /* BreakpadController.h */,
|
||||
16C92FAC150DF8330053D7BA /* BreakpadController.mm */,
|
||||
16BFA66A14E195E9009704F8 /* handler */,
|
||||
16C7C96A147D4A4200776EAD /* Breakpad.h */,
|
||||
16C7C96B147D4A4200776EAD /* Breakpad.mm */,
|
||||
);
|
||||
path = ios;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C99E147D4A4200776EAD /* mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CB9D147D4A4300776EAD /* crash_generation */,
|
||||
16C7CBAA147D4A4300776EAD /* handler */,
|
||||
16C7CBC8147D4A4300776EAD /* sender */,
|
||||
);
|
||||
path = mac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CB9D147D4A4300776EAD /* crash_generation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CB9E147D4A4300776EAD /* ConfigFile.h */,
|
||||
16C7CB9F147D4A4300776EAD /* ConfigFile.mm */,
|
||||
);
|
||||
path = crash_generation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CBAA147D4A4300776EAD /* handler */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */,
|
||||
16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */,
|
||||
16C7CBAF147D4A4300776EAD /* dynamic_images.cc */,
|
||||
16C7CBB0147D4A4300776EAD /* dynamic_images.h */,
|
||||
16C7CBB1147D4A4300776EAD /* exception_handler.cc */,
|
||||
16C7CBB2147D4A4300776EAD /* exception_handler.h */,
|
||||
14569322182CE2C10029C465 /* mach_vm_compat.h */,
|
||||
16C7CBB4147D4A4300776EAD /* minidump_generator.cc */,
|
||||
16C7CBB5147D4A4300776EAD /* minidump_generator.h */,
|
||||
16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */,
|
||||
16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */,
|
||||
14569320182CE29F0029C465 /* ucontext_compat.h */,
|
||||
);
|
||||
path = handler;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CBC8147D4A4300776EAD /* sender */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CBEA147D4A4300776EAD /* uploader.h */,
|
||||
16C7CBEB147D4A4300776EAD /* uploader.mm */,
|
||||
);
|
||||
path = sender;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7CC47147D4A4300776EAD /* common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */,
|
||||
1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */,
|
||||
16C7CC4A147D4A4300776EAD /* convert_UTF.c */,
|
||||
16C7CC4B147D4A4300776EAD /* convert_UTF.h */,
|
||||
16C7CC82147D4A4300776EAD /* mac */,
|
||||
16C7CCA4147D4A4300776EAD /* md5.cc */,
|
||||
16C7CCA5147D4A4300776EAD /* md5.h */,
|
||||
16C7CCB9147D4A4300776EAD /* string_conversion.cc */,
|
||||
16C7CCBA147D4A4300776EAD /* string_conversion.h */,
|
||||
);
|
||||
name = common;
|
||||
path = ../../common;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
16C7CC82147D4A4300776EAD /* mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7CC88147D4A4300776EAD /* GTMLogger.h */,
|
||||
16C7CC89147D4A4300776EAD /* GTMLogger.m */,
|
||||
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */,
|
||||
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */,
|
||||
16C7CC93147D4A4300776EAD /* file_id.cc */,
|
||||
16C7CC94147D4A4300776EAD /* file_id.h */,
|
||||
16C7CC95147D4A4300776EAD /* macho_id.cc */,
|
||||
16C7CC96147D4A4300776EAD /* macho_id.h */,
|
||||
16C7CC9A147D4A4300776EAD /* macho_utilities.cc */,
|
||||
16C7CC9B147D4A4300776EAD /* macho_utilities.h */,
|
||||
16C7CC9C147D4A4300776EAD /* macho_walker.cc */,
|
||||
16C7CC9D147D4A4300776EAD /* macho_walker.h */,
|
||||
16C7CC9F147D4A4300776EAD /* string_utilities.cc */,
|
||||
16C7CCA0147D4A4300776EAD /* string_utilities.h */,
|
||||
);
|
||||
path = mac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32C88DFF0371C24200C91783 /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
D2AAC07A0554694100DB518D /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */,
|
||||
16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */,
|
||||
16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */,
|
||||
16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */,
|
||||
14569321182CE29F0029C465 /* ucontext_compat.h in Headers */,
|
||||
16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */,
|
||||
16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */,
|
||||
16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */,
|
||||
16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */,
|
||||
16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */,
|
||||
16C7CE08147D4A4300776EAD /* uploader.h in Headers */,
|
||||
16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */,
|
||||
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */,
|
||||
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */,
|
||||
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */,
|
||||
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */,
|
||||
16C7CE84147D4A4300776EAD /* file_id.h in Headers */,
|
||||
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */,
|
||||
16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */,
|
||||
16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */,
|
||||
16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */,
|
||||
16C7CE94147D4A4300776EAD /* md5.h in Headers */,
|
||||
16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */,
|
||||
16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */,
|
||||
16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */,
|
||||
1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */,
|
||||
14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
D2AAC07D0554694100DB518D /* Breakpad */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
|
||||
buildPhases = (
|
||||
D2AAC07A0554694100DB518D /* Headers */,
|
||||
D2AAC07B0554694100DB518D /* Sources */,
|
||||
D2AAC07C0554694100DB518D /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Breakpad;
|
||||
productName = Breakpad;
|
||||
productReference = D2AAC07E0554694100DB518D /* libBreakpad.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
0867D690FE84028FC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
};
|
||||
buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
English,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
da,
|
||||
de,
|
||||
es,
|
||||
fr,
|
||||
it,
|
||||
ja,
|
||||
nl,
|
||||
no,
|
||||
sl,
|
||||
sv,
|
||||
tr,
|
||||
);
|
||||
mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
|
||||
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
D2AAC07D0554694100DB518D /* Breakpad */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
D2AAC07B0554694100DB518D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */,
|
||||
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */,
|
||||
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */,
|
||||
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */,
|
||||
16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */,
|
||||
16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */,
|
||||
16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */,
|
||||
16C7CE09147D4A4300776EAD /* uploader.mm in Sources */,
|
||||
16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */,
|
||||
16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */,
|
||||
16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */,
|
||||
16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */,
|
||||
16C7CE83147D4A4300776EAD /* file_id.cc in Sources */,
|
||||
16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */,
|
||||
16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */,
|
||||
16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */,
|
||||
16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */,
|
||||
16C7CE93147D4A4300776EAD /* md5.cc in Sources */,
|
||||
16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */,
|
||||
16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */,
|
||||
16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */,
|
||||
1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB921F08733DC00010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DSTROOT = /tmp/Breakpad.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
);
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
|
||||
INSTALL_PATH = /usr/local/lib;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
"\"$(SRCROOT)/../mac/gcov\"",
|
||||
);
|
||||
PRODUCT_NAME = Breakpad;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB922008733DC00010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
DSTROOT = /tmp/Breakpad.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
);
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
|
||||
INSTALL_PATH = /usr/local/lib;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
|
||||
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
|
||||
"\"$(SRCROOT)/../mac/build/Debug\"",
|
||||
"\"$(SRCROOT)/../mac/gcov\"",
|
||||
);
|
||||
PRODUCT_NAME = Breakpad;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB922308733DC00010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../,
|
||||
../../client/apple/Framework,
|
||||
../../common/mac,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
SDKROOT = iphoneos;
|
||||
WARNING_CFLAGS = "-Wundef";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB922408733DC00010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../,
|
||||
../../client/apple/Framework,
|
||||
../../common/mac,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
SDKROOT = iphoneos;
|
||||
WARNING_CFLAGS = "-Wundef";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB921F08733DC00010E9CD /* Debug */,
|
||||
1DEB922008733DC00010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB922308733DC00010E9CD /* Debug */,
|
||||
1DEB922408733DC00010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
|
||||
#define CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "client/ios/Breakpad.h"
|
||||
|
||||
// This class is used to offer a higher level API around BreakpadRef. It
|
||||
// configures it, ensures thread-safety, and sends crash reports back to the
|
||||
// collecting server. By default, no crash reports are sent, the user must call
|
||||
// |setUploadingEnabled:YES| to start the uploading.
|
||||
@interface BreakpadController : NSObject {
|
||||
@private
|
||||
// The dispatch queue that will own the breakpad reference.
|
||||
dispatch_queue_t queue_;
|
||||
|
||||
// Instance of Breakpad crash reporter. This is owned by the queue, but can
|
||||
// be created on the main thread at startup.
|
||||
BreakpadRef breakpadRef_;
|
||||
|
||||
// The dictionary that contains configuration for breakpad. Modifying it
|
||||
// should only happen when the controller is not started. The initial value
|
||||
// is the infoDictionary of the bundle of the application.
|
||||
NSMutableDictionary* configuration_;
|
||||
|
||||
// Whether or not crash reports should be uploaded.
|
||||
BOOL enableUploads_;
|
||||
|
||||
// Whether the controller has been started on the main thread. This is only
|
||||
// used to assert the initialization order is correct.
|
||||
BOOL started_;
|
||||
|
||||
// The interval to wait between two uploads. Value is 0 if no upload must be
|
||||
// done.
|
||||
int uploadIntervalInSeconds_;
|
||||
|
||||
// The dictionary that contains additional server parameters to send when
|
||||
// uploading crash reports.
|
||||
NSDictionary* uploadTimeParameters_;
|
||||
}
|
||||
|
||||
// Singleton.
|
||||
+ (BreakpadController*)sharedInstance;
|
||||
|
||||
// Update the controller configuration. Merges its old configuration with the
|
||||
// new one. Merge is done by replacing the old values by the new values.
|
||||
- (void)updateConfiguration:(NSDictionary*)configuration;
|
||||
|
||||
// Reset the controller configuration to its initial value, which is the
|
||||
// infoDictionary of the bundle of the application.
|
||||
- (void)resetConfiguration;
|
||||
|
||||
// Configure the URL to upload the report to. This must be called at least once
|
||||
// if the URL is not in the bundle information.
|
||||
- (void)setUploadingURL:(NSString*)url;
|
||||
|
||||
// Set the minimal interval between two uploads in seconds. This must be called
|
||||
// at least once if the interval is not in the bundle information. A value of 0
|
||||
// will prevent uploads.
|
||||
- (void)setUploadInterval:(int)intervalInSeconds;
|
||||
|
||||
// Set additional server parameters to send when uploading crash reports.
|
||||
- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters;
|
||||
|
||||
// Specify an upload parameter that will be added to the crash report when a
|
||||
// crash report is generated. See |BreakpadAddUploadParameter|.
|
||||
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key;
|
||||
|
||||
// Remove a previously-added parameter from the upload parameter set. See
|
||||
// |BreakpadRemoveUploadParameter|.
|
||||
- (void)removeUploadParameterForKey:(NSString*)key;
|
||||
|
||||
// Access the underlying BreakpadRef. This method is asynchronous, and will be
|
||||
// executed on the thread owning the BreakpadRef variable. Moreover, if the
|
||||
// controller is not started, the block will be called with a NULL parameter.
|
||||
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback;
|
||||
|
||||
// Starts the BreakpadController by registering crash handlers. If
|
||||
// |onCurrentThread| is YES, all setup is done on the current thread, otherwise
|
||||
// it is done on a private queue.
|
||||
- (void)start:(BOOL)onCurrentThread;
|
||||
|
||||
// Unregisters the crash handlers.
|
||||
- (void)stop;
|
||||
|
||||
// Enables or disables uploading of crash reports, but does not stop the
|
||||
// BreakpadController.
|
||||
- (void)setUploadingEnabled:(BOOL)enabled;
|
||||
|
||||
// Check if there is currently a crash report to upload.
|
||||
- (void)hasReportToUpload:(void(^)(BOOL))callback;
|
||||
|
||||
// Get the number of crash reports waiting to upload.
|
||||
- (void)getCrashReportCount:(void(^)(int))callback;
|
||||
|
||||
// Get the next report to upload.
|
||||
// - If upload is disabled, callback will be called with (nil, -1).
|
||||
// - If a delay is to be waited before sending, callback will be called with
|
||||
// (nil, n), with n (> 0) being the number of seconds to wait.
|
||||
// - if no delay is needed, callback will be called with (0, configuration),
|
||||
// configuration being next report to upload, or nil if none is pending.
|
||||
- (void)getNextReportConfigurationOrSendDelay:
|
||||
(void(^)(NSDictionary*, int))callback;
|
||||
|
||||
// Sends synchronously the report specified by |configuration|. This method is
|
||||
// NOT thread safe and must be called from the breakpad thread.
|
||||
- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
|
||||
withBreakpadRef:(BreakpadRef)ref;
|
||||
|
||||
@end
|
||||
|
||||
#endif // CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
|
@ -1,354 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import "BreakpadController.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <asl.h>
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <common/scoped_ptr.h>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private Methods
|
||||
|
||||
@interface BreakpadController ()
|
||||
|
||||
// Init the singleton instance.
|
||||
- (id)initSingleton;
|
||||
|
||||
// Load a crash report and send it to the server.
|
||||
- (void)sendStoredCrashReports;
|
||||
|
||||
// Returns when a report can be sent. |-1| means never, |0| means that a report
|
||||
// can be sent immediately, a positive number is the number of seconds to wait
|
||||
// before being allowed to upload a report.
|
||||
- (int)sendDelay;
|
||||
|
||||
// Notifies that a report will be sent, and update the last sending time
|
||||
// accordingly.
|
||||
- (void)reportWillBeSent;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Anonymous namespace
|
||||
|
||||
namespace {
|
||||
|
||||
// The name of the user defaults key for the last submission to the crash
|
||||
// server.
|
||||
NSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission";
|
||||
|
||||
// Returns a NSString describing the current platform.
|
||||
NSString* GetPlatform() {
|
||||
// Name of the system call for getting the platform.
|
||||
static const char kHwMachineSysctlName[] = "hw.machine";
|
||||
|
||||
NSString* result = nil;
|
||||
|
||||
size_t size = 0;
|
||||
if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0)
|
||||
return nil;
|
||||
google_breakpad::scoped_array<char> machine(new char[size]);
|
||||
if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0)
|
||||
result = [NSString stringWithUTF8String:machine.get()];
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark BreakpadController Implementation
|
||||
|
||||
@implementation BreakpadController
|
||||
|
||||
+ (BreakpadController*)sharedInstance {
|
||||
@synchronized(self) {
|
||||
static BreakpadController* sharedInstance_ =
|
||||
[[BreakpadController alloc] initSingleton];
|
||||
return sharedInstance_;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initSingleton {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL);
|
||||
enableUploads_ = NO;
|
||||
started_ = NO;
|
||||
[self resetConfiguration];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Since this class is a singleton, this method is not expected to be called.
|
||||
- (void)dealloc {
|
||||
assert(!breakpadRef_);
|
||||
dispatch_release(queue_);
|
||||
[configuration_ release];
|
||||
[uploadTimeParameters_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)start:(BOOL)onCurrentThread {
|
||||
if (started_)
|
||||
return;
|
||||
started_ = YES;
|
||||
void(^startBlock)() = ^{
|
||||
assert(!breakpadRef_);
|
||||
breakpadRef_ = BreakpadCreate(configuration_);
|
||||
if (breakpadRef_) {
|
||||
BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform());
|
||||
}
|
||||
};
|
||||
if (onCurrentThread)
|
||||
startBlock();
|
||||
else
|
||||
dispatch_async(queue_, startBlock);
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
if (!started_)
|
||||
return;
|
||||
started_ = NO;
|
||||
dispatch_sync(queue_, ^{
|
||||
if (breakpadRef_) {
|
||||
BreakpadRelease(breakpadRef_);
|
||||
breakpadRef_ = NULL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This method must be called from the breakpad queue.
|
||||
- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
|
||||
withBreakpadRef:(BreakpadRef)ref {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"threadUnsafeSendReportWithConfiguration is called");
|
||||
if (breakpadRef_) {
|
||||
BreakpadUploadReportWithParametersAndConfiguration(breakpadRef_,
|
||||
uploadTimeParameters_,
|
||||
configuration);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUploadingEnabled:(BOOL)enabled {
|
||||
NSAssert(started_,
|
||||
@"The controller must be started before setUploadingEnabled is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (enabled == enableUploads_)
|
||||
return;
|
||||
if (enabled) {
|
||||
// Set this before calling doSendStoredCrashReport, because that
|
||||
// calls sendDelay, which in turn checks this flag.
|
||||
enableUploads_ = YES;
|
||||
[self sendStoredCrashReports];
|
||||
} else {
|
||||
enableUploads_ = NO;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
||||
selector:@selector(sendStoredCrashReports)
|
||||
object:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateConfiguration:(NSDictionary*)configuration {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when updateConfiguration is called");
|
||||
[configuration_ addEntriesFromDictionary:configuration];
|
||||
NSString* uploadInterval =
|
||||
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
|
||||
if (uploadInterval)
|
||||
[self setUploadInterval:[uploadInterval intValue]];
|
||||
}
|
||||
|
||||
- (void)resetConfiguration {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when resetConfiguration is called");
|
||||
[configuration_ autorelease];
|
||||
configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy];
|
||||
NSString* uploadInterval =
|
||||
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
|
||||
[self setUploadInterval:[uploadInterval intValue]];
|
||||
[self setParametersToAddAtUploadTime:nil];
|
||||
}
|
||||
|
||||
- (void)setUploadingURL:(NSString*)url {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when setUploadingURL is called");
|
||||
[configuration_ setValue:url forKey:@BREAKPAD_URL];
|
||||
}
|
||||
|
||||
- (void)setUploadInterval:(int)intervalInSeconds {
|
||||
NSAssert(!started_,
|
||||
@"The controller must not be started when setUploadInterval is called");
|
||||
[configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL];
|
||||
uploadIntervalInSeconds_ = intervalInSeconds;
|
||||
if (uploadIntervalInSeconds_ < 0)
|
||||
uploadIntervalInSeconds_ = 0;
|
||||
}
|
||||
|
||||
- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters {
|
||||
NSAssert(!started_, @"The controller must not be started when "
|
||||
"setParametersToAddAtUploadTime is called");
|
||||
[uploadTimeParameters_ autorelease];
|
||||
uploadTimeParameters_ = [uploadTimeParameters copy];
|
||||
}
|
||||
|
||||
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key {
|
||||
NSAssert(started_,
|
||||
@"The controller must be started before addUploadParameter is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (breakpadRef_)
|
||||
BreakpadAddUploadParameter(breakpadRef_, key, value);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeUploadParameterForKey:(NSString*)key {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"removeUploadParameterForKey is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (breakpadRef_)
|
||||
BreakpadRemoveUploadParameter(breakpadRef_, key);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback {
|
||||
NSAssert(started_,
|
||||
@"The controller must be started before withBreakpadRef is called");
|
||||
dispatch_async(queue_, ^{
|
||||
callback(breakpadRef_);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)hasReportToUpload:(void(^)(BOOL))callback {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"hasReportToUpload is called");
|
||||
dispatch_async(queue_, ^{
|
||||
callback(breakpadRef_ && (BreakpadGetCrashReportCount(breakpadRef_) > 0));
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getCrashReportCount:(void(^)(int))callback {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"getCrashReportCount is called");
|
||||
dispatch_async(queue_, ^{
|
||||
callback(breakpadRef_ ? BreakpadGetCrashReportCount(breakpadRef_) : 0);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getNextReportConfigurationOrSendDelay:
|
||||
(void(^)(NSDictionary*, int))callback {
|
||||
NSAssert(started_, @"The controller must be started before "
|
||||
"getNextReportConfigurationOrSendDelay is called");
|
||||
dispatch_async(queue_, ^{
|
||||
if (!breakpadRef_) {
|
||||
callback(nil, -1);
|
||||
return;
|
||||
}
|
||||
int delay = [self sendDelay];
|
||||
if (delay != 0) {
|
||||
callback(nil, delay);
|
||||
return;
|
||||
}
|
||||
[self reportWillBeSent];
|
||||
callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (int)sendDelay {
|
||||
if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_)
|
||||
return -1;
|
||||
|
||||
// To prevent overloading the crash server, crashes are not sent than one
|
||||
// report every |uploadIntervalInSeconds_|. A value in the user defaults is
|
||||
// used to keep the time of the last upload.
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission];
|
||||
NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
|
||||
NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime;
|
||||
|
||||
if (spanSeconds >= uploadIntervalInSeconds_)
|
||||
return 0;
|
||||
return uploadIntervalInSeconds_ - static_cast<int>(spanSeconds);
|
||||
}
|
||||
|
||||
- (void)reportWillBeSent {
|
||||
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||
[userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()]
|
||||
forKey:kLastSubmission];
|
||||
[userDefaults synchronize];
|
||||
}
|
||||
|
||||
- (void)sendStoredCrashReports {
|
||||
dispatch_async(queue_, ^{
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) == 0)
|
||||
return;
|
||||
|
||||
int timeToWait = [self sendDelay];
|
||||
|
||||
// Unable to ever send report.
|
||||
if (timeToWait == -1)
|
||||
return;
|
||||
|
||||
// A report can be sent now.
|
||||
if (timeToWait == 0) {
|
||||
[self reportWillBeSent];
|
||||
BreakpadUploadNextReportWithParameters(breakpadRef_,
|
||||
uploadTimeParameters_);
|
||||
|
||||
// If more reports must be sent, make sure this method is called again.
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) > 0)
|
||||
timeToWait = uploadIntervalInSeconds_;
|
||||
}
|
||||
|
||||
// A report must be sent later.
|
||||
if (timeToWait > 0) {
|
||||
// performSelector: doesn't work on queue_
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self performSelector:@selector(sendStoredCrashReports)
|
||||
withObject:nil
|
||||
afterDelay:timeToWait];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
@ -1,7 +0,0 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'CocoaTouchStaticLibrary' target in the 'CocoaTouchStaticLibrary' project.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
@ -1,74 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ios_exception_minidump_generator.h: Create a fake minidump from a
|
||||
// NSException.
|
||||
|
||||
#ifndef CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
|
||||
#define CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class IosExceptionMinidumpGenerator : public MinidumpGenerator {
|
||||
public:
|
||||
explicit IosExceptionMinidumpGenerator(NSException *exception);
|
||||
virtual ~IosExceptionMinidumpGenerator();
|
||||
|
||||
protected:
|
||||
virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
|
||||
virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
|
||||
|
||||
private:
|
||||
|
||||
// Get the crashing program counter from the exception.
|
||||
uintptr_t GetPCFromException();
|
||||
|
||||
// Get the crashing link register from the exception.
|
||||
uintptr_t GetLRFromException();
|
||||
|
||||
// Write a virtual thread context for the crashing site.
|
||||
bool WriteCrashingContext(MDLocationDescriptor *register_location);
|
||||
// Per-CPU implementations of the above method.
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
bool WriteCrashingContextARM(MDLocationDescriptor *register_location);
|
||||
#endif
|
||||
#ifdef HAS_ARM64_SUPPORT
|
||||
bool WriteCrashingContextARM64(MDLocationDescriptor *register_location);
|
||||
#endif
|
||||
|
||||
NSArray *return_addresses_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
|
@ -1,210 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/ios/handler/ios_exception_minidump_generator.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_cpu_arm.h"
|
||||
#include "google_breakpad/common/minidump_cpu_arm64.h"
|
||||
#include "google_breakpad/common/minidump_exception_mac.h"
|
||||
#include "client/minidump_file_writer-inl.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
|
||||
#if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT)
|
||||
#error "This file should be compiled for only one architecture at a time"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
const int kExceptionType = EXC_SOFTWARE;
|
||||
const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION;
|
||||
|
||||
#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
|
||||
const uintptr_t kExpectedFinalFp = sizeof(uintptr_t);
|
||||
const uintptr_t kExpectedFinalSp = 0;
|
||||
|
||||
// Append the given value to the sp position of the stack represented
|
||||
// by memory.
|
||||
void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) {
|
||||
memcpy(memory + sp, &data, sizeof(data));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator(
|
||||
NSException *exception)
|
||||
: MinidumpGenerator(mach_task_self(), 0) {
|
||||
return_addresses_ = [[exception callStackReturnAddresses] retain];
|
||||
SetExceptionInformation(kExceptionType,
|
||||
kExceptionCode,
|
||||
0,
|
||||
pthread_mach_thread_np(pthread_self()));
|
||||
}
|
||||
|
||||
IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() {
|
||||
[return_addresses_ release];
|
||||
}
|
||||
|
||||
bool IosExceptionMinidumpGenerator::WriteCrashingContext(
|
||||
MDLocationDescriptor *register_location) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
return WriteCrashingContextARM(register_location);
|
||||
#elif defined(HAS_ARM64_SUPPORT)
|
||||
return WriteCrashingContextARM64(register_location);
|
||||
#else
|
||||
assert(false);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
bool IosExceptionMinidumpGenerator::WriteCrashingContextARM(
|
||||
MDLocationDescriptor *register_location) {
|
||||
TypedMDRVA<MDRawContextARM> context(&writer_);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
*register_location = context.location();
|
||||
MDRawContextARM *context_ptr = context.get();
|
||||
memset(context_ptr, 0, sizeof(MDRawContextARM));
|
||||
context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp; // FP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp; // SP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException(); // LR
|
||||
context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException(); // PC
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ARM64_SUPPORT
|
||||
bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64(
|
||||
MDLocationDescriptor *register_location) {
|
||||
TypedMDRVA<MDRawContextARM64> context(&writer_);
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
*register_location = context.location();
|
||||
MDRawContextARM64 *context_ptr = context.get();
|
||||
memset(context_ptr, 0, sizeof(*context_ptr));
|
||||
context_ptr->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp; // FP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp; // SP
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException(); // LR
|
||||
context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException(); // PC
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() {
|
||||
return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
|
||||
}
|
||||
|
||||
uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() {
|
||||
return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue];
|
||||
}
|
||||
|
||||
bool IosExceptionMinidumpGenerator::WriteExceptionStream(
|
||||
MDRawDirectory *exception_stream) {
|
||||
#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
|
||||
TypedMDRVA<MDRawExceptionStream> exception(&writer_);
|
||||
|
||||
if (!exception.Allocate())
|
||||
return false;
|
||||
|
||||
exception_stream->stream_type = MD_EXCEPTION_STREAM;
|
||||
exception_stream->location = exception.location();
|
||||
MDRawExceptionStream *exception_ptr = exception.get();
|
||||
exception_ptr->thread_id = pthread_mach_thread_np(pthread_self());
|
||||
|
||||
// This naming is confusing, but it is the proper translation from
|
||||
// mach naming to minidump naming.
|
||||
exception_ptr->exception_record.exception_code = kExceptionType;
|
||||
exception_ptr->exception_record.exception_flags = kExceptionCode;
|
||||
|
||||
if (!WriteCrashingContext(&exception_ptr->thread_context))
|
||||
return false;
|
||||
|
||||
exception_ptr->exception_record.exception_address = GetPCFromException();
|
||||
return true;
|
||||
#else
|
||||
return MinidumpGenerator::WriteExceptionStream(exception_stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
|
||||
MDRawThread *thread) {
|
||||
#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
|
||||
if (pthread_mach_thread_np(pthread_self()) != thread_id)
|
||||
return MinidumpGenerator::WriteThreadStream(thread_id, thread);
|
||||
|
||||
size_t frame_count = [return_addresses_ count];
|
||||
if (frame_count == 0)
|
||||
return false;
|
||||
UntypedMDRVA memory(&writer_);
|
||||
size_t pointer_size = sizeof(uintptr_t);
|
||||
size_t frame_record_size = 2 * pointer_size;
|
||||
size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size;
|
||||
if (!memory.Allocate(stack_size))
|
||||
return false;
|
||||
scoped_array<uint8_t> stack_memory(new uint8_t[stack_size]);
|
||||
uintptr_t sp = stack_size - pointer_size;
|
||||
uintptr_t fp = 0;
|
||||
uintptr_t lr = 0;
|
||||
for (size_t current_frame = frame_count - 1;
|
||||
current_frame > 0;
|
||||
--current_frame) {
|
||||
AppendToMemory(stack_memory.get(), sp, lr);
|
||||
sp -= pointer_size;
|
||||
AppendToMemory(stack_memory.get(), sp, fp);
|
||||
fp = sp;
|
||||
sp -= pointer_size;
|
||||
lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue];
|
||||
}
|
||||
if (!memory.Copy(stack_memory.get(), stack_size))
|
||||
return false;
|
||||
assert(sp == kExpectedFinalSp);
|
||||
assert(fp == kExpectedFinalFp);
|
||||
assert(lr == GetLRFromException());
|
||||
thread->stack.start_of_memory_range = sp;
|
||||
thread->stack.memory = memory.location();
|
||||
memory_blocks_.push_back(thread->stack);
|
||||
|
||||
if (!WriteCrashingContext(&thread->thread_context))
|
||||
return false;
|
||||
|
||||
thread->thread_id = thread_id;
|
||||
return true;
|
||||
#else
|
||||
return MinidumpGenerator::WriteThreadStream(thread_id, thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2016, Google Inc.
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@ -27,17 +27,18 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
|
||||
#define GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
|
||||
// The Android NDK doesn't have link.h. Fortunately, the only thing
|
||||
// that Breakpad uses from it is the ElfW macro, so define it here.
|
||||
|
||||
#include <stdio.h>
|
||||
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
|
||||
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
|
||||
|
||||
#if defined(_MSC_VER) && MSC_VER < 1900
|
||||
#include <basetsd.h>
|
||||
#include <sys/exec_elf.h>
|
||||
|
||||
#define snprintf _snprintf
|
||||
typedef SSIZE_T ssize_t;
|
||||
#ifndef ElfW
|
||||
#define ElfW(type) _ElfW (Elf, ELFSIZE, type)
|
||||
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
|
||||
#define _ElfW_1(e,w,t) e##w##t
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// Copyright (c) 2009, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@ -27,9 +27,51 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
|
||||
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
|
||||
// Android runs a fairly new Linux kernel, so signal info is there,
|
||||
// but the NDK doesn't have the structs defined, so define
|
||||
// them here.
|
||||
// Adapted from platform-linux.cc in V8
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
|
||||
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
|
||||
#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
|
||||
|
||||
struct sigcontext {
|
||||
uint32_t trap_no;
|
||||
uint32_t error_code;
|
||||
uint32_t oldmask;
|
||||
uint32_t arm_r0;
|
||||
uint32_t arm_r1;
|
||||
uint32_t arm_r2;
|
||||
uint32_t arm_r3;
|
||||
uint32_t arm_r4;
|
||||
uint32_t arm_r5;
|
||||
uint32_t arm_r6;
|
||||
uint32_t arm_r7;
|
||||
uint32_t arm_r8;
|
||||
uint32_t arm_r9;
|
||||
uint32_t arm_r10;
|
||||
uint32_t arm_fp;
|
||||
uint32_t arm_ip;
|
||||
uint32_t arm_sp;
|
||||
uint32_t arm_lr;
|
||||
uint32_t arm_pc;
|
||||
uint32_t arm_cpsr;
|
||||
uint32_t fault_address;
|
||||
};
|
||||
typedef uint32_t __sigset_t;
|
||||
typedef struct sigcontext mcontext_t;
|
||||
typedef struct ucontext {
|
||||
uint32_t uc_flags;
|
||||
struct ucontext* uc_link;
|
||||
stack_t uc_stack;
|
||||
mcontext_t uc_mcontext;
|
||||
__sigset_t uc_sigmask;
|
||||
} ucontext_t;
|
||||
|
||||
#endif
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
|
@ -34,16 +34,7 @@ namespace google_breakpad {
|
||||
|
||||
class CrashGenerationServer;
|
||||
|
||||
class ClientInfo {
|
||||
public:
|
||||
ClientInfo(pid_t pid, CrashGenerationServer* crash_server)
|
||||
: crash_server_(crash_server),
|
||||
pid_(pid) {}
|
||||
|
||||
CrashGenerationServer* crash_server() const { return crash_server_; }
|
||||
pid_t pid() const { return pid_; }
|
||||
|
||||
private:
|
||||
struct ClientInfo {
|
||||
CrashGenerationServer* crash_server_;
|
||||
pid_t pid_;
|
||||
};
|
||||
|
@ -27,79 +27,63 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
bool
|
||||
CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
|
||||
{
|
||||
int fds[2];
|
||||
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
|
||||
|
||||
class CrashGenerationClientImpl : public CrashGenerationClient {
|
||||
public:
|
||||
explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {}
|
||||
virtual ~CrashGenerationClientImpl() {}
|
||||
struct kernel_msghdr msg;
|
||||
my_memset(&msg, 0, sizeof(struct kernel_msghdr));
|
||||
struct kernel_iovec iov[1];
|
||||
iov[0].iov_base = const_cast<void*>(blob);
|
||||
iov[0].iov_len = blob_size;
|
||||
|
||||
virtual bool RequestDump(const void* blob, size_t blob_size) {
|
||||
int fds[2];
|
||||
if (sys_pipe(fds) < 0)
|
||||
return false;
|
||||
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
|
||||
char cmsg[kControlMsgSize];
|
||||
my_memset(cmsg, 0, kControlMsgSize);
|
||||
msg.msg_control = cmsg;
|
||||
msg.msg_controllen = sizeof(cmsg);
|
||||
|
||||
struct kernel_iovec iov;
|
||||
iov.iov_base = const_cast<void*>(blob);
|
||||
iov.iov_len = blob_size;
|
||||
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
|
||||
hdr->cmsg_level = SOL_SOCKET;
|
||||
hdr->cmsg_type = SCM_RIGHTS;
|
||||
hdr->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
|
||||
*p = fds[1];
|
||||
|
||||
struct kernel_msghdr msg = { 0 };
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
char cmsg[kControlMsgSize] = "";
|
||||
msg.msg_control = cmsg;
|
||||
msg.msg_controllen = sizeof(cmsg);
|
||||
HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
||||
sys_close(fds[1]);
|
||||
|
||||
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
|
||||
hdr->cmsg_level = SOL_SOCKET;
|
||||
hdr->cmsg_type = SCM_RIGHTS;
|
||||
hdr->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
|
||||
*p = fds[1];
|
||||
// wait for an ACK from the server
|
||||
char b;
|
||||
HANDLE_EINTR(sys_read(fds[0], &b, 1));
|
||||
|
||||
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
||||
sys_close(fds[1]);
|
||||
if (ret < 0) {
|
||||
sys_close(fds[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for an ACK from the server.
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
|
||||
sys_close(fds[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
int server_fd_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) {
|
||||
if (server_fd < 0)
|
||||
return NULL;
|
||||
return new CrashGenerationClientImpl(server_fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
//static
|
||||
CrashGenerationClient*
|
||||
CrashGenerationClient::TryCreate(int server_fd)
|
||||
{
|
||||
if (0 > server_fd)
|
||||
return NULL;
|
||||
return new CrashGenerationClient(server_fd);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,36 +30,40 @@
|
||||
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
|
||||
#include "common/basictypes.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// CrashGenerationClient is an interface for implementing out-of-process crash
|
||||
// dumping. The default implementation, accessed via the TryCreate() factory,
|
||||
// works in conjunction with the CrashGenerationServer to generate a minidump
|
||||
// via a remote process.
|
||||
class CrashGenerationClient {
|
||||
public:
|
||||
CrashGenerationClient() {}
|
||||
virtual ~CrashGenerationClient() {}
|
||||
public:
|
||||
~CrashGenerationClient()
|
||||
{
|
||||
}
|
||||
|
||||
// Request the crash server to generate a dump. |blob| is an opaque
|
||||
// CrashContext pointer from exception_handler.h.
|
||||
// Returns true if the dump was successful; false otherwise.
|
||||
virtual bool RequestDump(const void* blob, size_t blob_size) = 0;
|
||||
// Request the crash server to generate a dump. |blob| is a hack,
|
||||
// see exception_handler.h and minidump_writer.h
|
||||
//
|
||||
// Return true if the dump was successful; false otherwise.
|
||||
bool RequestDump(const void* blob, size_t blob_size);
|
||||
|
||||
// Returns a new CrashGenerationClient if |server_fd| is valid and
|
||||
// Return a new CrashGenerationClient if |server_fd| is valid and
|
||||
// connects to a CrashGenerationServer. Otherwise, return NULL.
|
||||
// The returned CrashGenerationClient* is owned by the caller of
|
||||
// this function.
|
||||
static CrashGenerationClient* TryCreate(int server_fd);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient);
|
||||
private:
|
||||
CrashGenerationClient(int server_fd) : server_fd_(server_fd)
|
||||
{
|
||||
}
|
||||
|
||||
int server_fd_;
|
||||
|
||||
// prevent copy construction and assignment
|
||||
CrashGenerationClient(const CrashGenerationClient&);
|
||||
CrashGenerationClient& operator=(const CrashGenerationClient&);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
|
@ -47,10 +47,121 @@
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
|
||||
static const char kCommandQuit = 'x';
|
||||
|
||||
static bool
|
||||
GetInodeForFileDescriptor(ino_t* inode_out, int fd)
|
||||
{
|
||||
assert(inode_out);
|
||||
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) < 0)
|
||||
return false;
|
||||
|
||||
if (!S_ISSOCK(buf.st_mode))
|
||||
return false;
|
||||
|
||||
*inode_out = buf.st_ino;
|
||||
return true;
|
||||
}
|
||||
|
||||
// expected prefix of the target of the /proc/self/fd/%d link for a socket
|
||||
static const char kSocketLinkPrefix[] = "socket:[";
|
||||
|
||||
// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
|
||||
// socket.
|
||||
// inode_out: (output) set to the inode number on success
|
||||
// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
|
||||
static bool
|
||||
GetInodeForProcPath(ino_t* inode_out, const char* path)
|
||||
{
|
||||
assert(inode_out);
|
||||
assert(path);
|
||||
|
||||
char buf[256];
|
||||
const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
|
||||
if (n == -1) {
|
||||
return false;
|
||||
}
|
||||
buf[n] = 0;
|
||||
|
||||
if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* endptr;
|
||||
const u_int64_t inode_ul =
|
||||
strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
|
||||
if (*endptr != ']')
|
||||
return false;
|
||||
|
||||
if (inode_ul == ULLONG_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*inode_out = inode_ul;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode)
|
||||
{
|
||||
assert(pid_out);
|
||||
bool already_found = false;
|
||||
|
||||
DIR* proc = opendir("/proc");
|
||||
if (!proc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<pid_t> pids;
|
||||
|
||||
struct dirent* dent;
|
||||
while ((dent = readdir(proc))) {
|
||||
char* endptr;
|
||||
const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
|
||||
if (pid_ul == ULONG_MAX || '\0' != *endptr)
|
||||
continue;
|
||||
pids.push_back(pid_ul);
|
||||
}
|
||||
closedir(proc);
|
||||
|
||||
for (std::vector<pid_t>::const_iterator
|
||||
i = pids.begin(); i != pids.end(); ++i) {
|
||||
const pid_t current_pid = *i;
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
|
||||
DIR* fd = opendir(buf);
|
||||
if (!fd)
|
||||
continue;
|
||||
|
||||
while ((dent = readdir(fd))) {
|
||||
if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
|
||||
dent->d_name) >= static_cast<int>(sizeof(buf))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ino_t fd_inode;
|
||||
if (GetInodeForProcPath(&fd_inode, buf)
|
||||
&& fd_inode == socket_inode) {
|
||||
if (already_found) {
|
||||
closedir(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
already_found = true;
|
||||
*pid_out = current_pid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(fd);
|
||||
}
|
||||
|
||||
return already_found;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
CrashGenerationServer::CrashGenerationServer(
|
||||
@ -60,7 +171,7 @@ CrashGenerationServer::CrashGenerationServer(
|
||||
OnClientExitingCallback exit_callback,
|
||||
void* exit_context,
|
||||
bool generate_dumps,
|
||||
const string* dump_path) :
|
||||
const std::string* dump_path) :
|
||||
server_fd_(listen_fd),
|
||||
dump_callback_(dump_callback),
|
||||
dump_context_(dump_context),
|
||||
@ -123,9 +234,6 @@ CrashGenerationServer::Stop()
|
||||
void* dummy;
|
||||
pthread_join(thread_, &dummy);
|
||||
|
||||
close(control_pipe_in_);
|
||||
close(control_pipe_out_);
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
@ -242,7 +350,7 @@ CrashGenerationServer::ClientEvent(short revents)
|
||||
// A nasty process could try and send us too many descriptors and
|
||||
// force a leak.
|
||||
for (unsigned i = 0; i < num_fds; ++i)
|
||||
close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
|
||||
HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]));
|
||||
return true;
|
||||
} else {
|
||||
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
|
||||
@ -256,30 +364,57 @@ CrashGenerationServer::ClientEvent(short revents)
|
||||
|
||||
if (crashing_pid == -1 || signal_fd == -1) {
|
||||
if (signal_fd)
|
||||
close(signal_fd);
|
||||
HANDLE_EINTR(close(signal_fd));
|
||||
return true;
|
||||
}
|
||||
|
||||
string minidump_filename;
|
||||
// Kernel bug workaround (broken in 2.6.30 at least):
|
||||
// The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
|
||||
// namespaces. Thus |crashing_pid| might be garbage from our point of view.
|
||||
// In the future we can remove this workaround, but we have to wait a couple
|
||||
// of years to be sure that it's worked its way out into the world.
|
||||
|
||||
ino_t inode_number;
|
||||
if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) {
|
||||
HANDLE_EINTR(close(signal_fd));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) {
|
||||
HANDLE_EINTR(close(signal_fd));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string minidump_filename;
|
||||
if (!MakeMinidumpFilename(minidump_filename))
|
||||
return true;
|
||||
|
||||
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
|
||||
crashing_pid, crash_context,
|
||||
kCrashContextSize)) {
|
||||
close(signal_fd);
|
||||
HANDLE_EINTR(close(signal_fd));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dump_callback_) {
|
||||
ClientInfo info(crashing_pid, this);
|
||||
ClientInfo info;
|
||||
|
||||
info.crash_server_ = this;
|
||||
info.pid_ = crashing_pid;
|
||||
|
||||
dump_callback_(dump_context_, &info, &minidump_filename);
|
||||
}
|
||||
|
||||
// Send the done signal to the process: it can exit now.
|
||||
// (Closing this will make the child's sys_read unblock and return 0.)
|
||||
close(signal_fd);
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
struct iovec done_iov;
|
||||
done_iov.iov_base = const_cast<char*>("\x42");
|
||||
done_iov.iov_len = 1;
|
||||
msg.msg_iov = &done_iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
|
||||
HANDLE_EINTR(close(signal_fd));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -306,7 +441,7 @@ CrashGenerationServer::ControlEvent(short revents)
|
||||
}
|
||||
|
||||
bool
|
||||
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
|
||||
CrashGenerationServer::MakeMinidumpFilename(std::string& outFilename)
|
||||
{
|
||||
GUID guid;
|
||||
char guidString[kGUIDStringLength+1];
|
||||
|
@ -34,8 +34,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ClientInfo;
|
||||
@ -47,7 +45,7 @@ public:
|
||||
// be thread safe.
|
||||
typedef void (*OnClientDumpRequestCallback)(void* context,
|
||||
const ClientInfo* client_info,
|
||||
const string* file_path);
|
||||
const std::string* file_path);
|
||||
|
||||
typedef void (*OnClientExitingCallback)(void* context,
|
||||
const ClientInfo* client_info);
|
||||
@ -71,7 +69,7 @@ public:
|
||||
OnClientExitingCallback exit_callback,
|
||||
void* exit_context,
|
||||
bool generate_dumps,
|
||||
const string* dump_path);
|
||||
const std::string* dump_path);
|
||||
|
||||
~CrashGenerationServer();
|
||||
|
||||
@ -102,7 +100,7 @@ private:
|
||||
bool ControlEvent(short revents);
|
||||
|
||||
// Return a unique filename at which a minidump can be written
|
||||
bool MakeMinidumpFilename(string& outFilename);
|
||||
bool MakeMinidumpFilename(std::string& outFilename);
|
||||
|
||||
// Trampoline to |Run()|
|
||||
static void* ThreadMain(void* arg);
|
||||
@ -117,7 +115,7 @@ private:
|
||||
|
||||
bool generate_dumps_;
|
||||
|
||||
string dump_dir_;
|
||||
std::string dump_dir_;
|
||||
|
||||
bool started_;
|
||||
|
||||
|
@ -1,61 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <list>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// One of these is produced for each mapping in the process (i.e. line in
|
||||
// /proc/$x/maps).
|
||||
struct MappingInfo {
|
||||
uintptr_t start_addr;
|
||||
size_t size;
|
||||
size_t offset; // offset into the backed file.
|
||||
bool exec; // true if the mapping has the execute bit set.
|
||||
char name[NAME_MAX];
|
||||
};
|
||||
|
||||
struct MappingEntry {
|
||||
MappingInfo first;
|
||||
uint8_t second[sizeof(MDGUID)];
|
||||
};
|
||||
|
||||
// A list of <MappingInfo, GUID>
|
||||
typedef std::list<MappingEntry> MappingList;
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
@ -1,53 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386__)
|
||||
typedef MDRawContextX86 RawContextCPU;
|
||||
#elif defined(__x86_64)
|
||||
typedef MDRawContextAMD64 RawContextCPU;
|
||||
#elif defined(__ARM_EABI__)
|
||||
typedef MDRawContextARM RawContextCPU;
|
||||
#elif defined(__aarch64__)
|
||||
typedef MDRawContextARM64 RawContextCPU;
|
||||
#elif defined(__mips__)
|
||||
typedef MDRawContextMIPS RawContextCPU;
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
@ -1,305 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(__i386__)
|
||||
// Write a uint16_t to memory
|
||||
// out: memory location to write to
|
||||
// v: value to write.
|
||||
void U16(void* out, uint16_t v) {
|
||||
my_memcpy(out, &v, sizeof(v));
|
||||
}
|
||||
|
||||
// Write a uint32_t to memory
|
||||
// out: memory location to write to
|
||||
// v: value to write.
|
||||
void U32(void* out, uint32_t v) {
|
||||
my_memcpy(out, &v, sizeof(v));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.eip;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_X86_ALL;
|
||||
|
||||
out->dr0 = dregs[0];
|
||||
out->dr1 = dregs[1];
|
||||
out->dr2 = dregs[2];
|
||||
out->dr3 = dregs[3];
|
||||
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
||||
// format.
|
||||
out->dr6 = dregs[6];
|
||||
out->dr7 = dregs[7];
|
||||
|
||||
out->gs = regs.xgs;
|
||||
out->fs = regs.xfs;
|
||||
out->es = regs.xes;
|
||||
out->ds = regs.xds;
|
||||
|
||||
out->edi = regs.edi;
|
||||
out->esi = regs.esi;
|
||||
out->ebx = regs.ebx;
|
||||
out->edx = regs.edx;
|
||||
out->ecx = regs.ecx;
|
||||
out->eax = regs.eax;
|
||||
|
||||
out->ebp = regs.ebp;
|
||||
out->eip = regs.eip;
|
||||
out->cs = regs.xcs;
|
||||
out->eflags = regs.eflags;
|
||||
out->esp = regs.esp;
|
||||
out->ss = regs.xss;
|
||||
|
||||
out->float_save.control_word = fpregs.cwd;
|
||||
out->float_save.status_word = fpregs.swd;
|
||||
out->float_save.tag_word = fpregs.twd;
|
||||
out->float_save.error_offset = fpregs.fip;
|
||||
out->float_save.error_selector = fpregs.fcs;
|
||||
out->float_save.data_offset = fpregs.foo;
|
||||
out->float_save.data_selector = fpregs.fos;
|
||||
|
||||
// 8 registers * 10 bytes per register.
|
||||
my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8);
|
||||
|
||||
// This matches the Intel fpsave format.
|
||||
U16(out->extended_registers + 0, fpregs.cwd);
|
||||
U16(out->extended_registers + 2, fpregs.swd);
|
||||
U16(out->extended_registers + 4, fpregs.twd);
|
||||
U16(out->extended_registers + 6, fpxregs.fop);
|
||||
U32(out->extended_registers + 8, fpxregs.fip);
|
||||
U16(out->extended_registers + 12, fpxregs.fcs);
|
||||
U32(out->extended_registers + 16, fpregs.foo);
|
||||
U16(out->extended_registers + 20, fpregs.fos);
|
||||
U32(out->extended_registers + 24, fpxregs.mxcsr);
|
||||
|
||||
my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128);
|
||||
my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128);
|
||||
}
|
||||
|
||||
#elif defined(__x86_64)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.rip;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_AMD64_FULL |
|
||||
MD_CONTEXT_AMD64_SEGMENTS;
|
||||
|
||||
out->cs = regs.cs;
|
||||
|
||||
out->ds = regs.ds;
|
||||
out->es = regs.es;
|
||||
out->fs = regs.fs;
|
||||
out->gs = regs.gs;
|
||||
|
||||
out->ss = regs.ss;
|
||||
out->eflags = regs.eflags;
|
||||
|
||||
out->dr0 = dregs[0];
|
||||
out->dr1 = dregs[1];
|
||||
out->dr2 = dregs[2];
|
||||
out->dr3 = dregs[3];
|
||||
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
||||
// format.
|
||||
out->dr6 = dregs[6];
|
||||
out->dr7 = dregs[7];
|
||||
|
||||
out->rax = regs.rax;
|
||||
out->rcx = regs.rcx;
|
||||
out->rdx = regs.rdx;
|
||||
out->rbx = regs.rbx;
|
||||
|
||||
out->rsp = regs.rsp;
|
||||
|
||||
out->rbp = regs.rbp;
|
||||
out->rsi = regs.rsi;
|
||||
out->rdi = regs.rdi;
|
||||
out->r8 = regs.r8;
|
||||
out->r9 = regs.r9;
|
||||
out->r10 = regs.r10;
|
||||
out->r11 = regs.r11;
|
||||
out->r12 = regs.r12;
|
||||
out->r13 = regs.r13;
|
||||
out->r14 = regs.r14;
|
||||
out->r15 = regs.r15;
|
||||
|
||||
out->rip = regs.rip;
|
||||
|
||||
out->flt_save.control_word = fpregs.cwd;
|
||||
out->flt_save.status_word = fpregs.swd;
|
||||
out->flt_save.tag_word = fpregs.ftw;
|
||||
out->flt_save.error_opcode = fpregs.fop;
|
||||
out->flt_save.error_offset = fpregs.rip;
|
||||
out->flt_save.error_selector = 0; // We don't have this.
|
||||
out->flt_save.data_offset = fpregs.rdp;
|
||||
out->flt_save.data_selector = 0; // We don't have this.
|
||||
out->flt_save.mx_csr = fpregs.mxcsr;
|
||||
out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
|
||||
|
||||
my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16);
|
||||
my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16);
|
||||
}
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.uregs[15];
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
|
||||
out->iregs[i] = regs.uregs[i];
|
||||
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
|
||||
out->cpsr = 0;
|
||||
#if !defined(__ANDROID__)
|
||||
out->float_save.fpscr = fpregs.fpsr |
|
||||
(static_cast<uint64_t>(fpregs.fpcr) << 32);
|
||||
// TODO: sort this out, actually collect floating point registers
|
||||
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.pc;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
|
||||
out->cpsr = static_cast<uint32_t>(regs.pstate);
|
||||
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
|
||||
out->iregs[i] = regs.regs[i];
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp;
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc;
|
||||
|
||||
out->float_save.fpsr = fpregs.fpsr;
|
||||
out->float_save.fpcr = fpregs.fpcr;
|
||||
my_memcpy(&out->float_save.regs, &fpregs.vregs,
|
||||
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
|
||||
}
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return mcontext.pc;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
#else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = mcontext.gregs[i];
|
||||
|
||||
out->mdhi = mcontext.mdhi;
|
||||
out->mdlo = mcontext.mdlo;
|
||||
out->dsp_control = mcontext.dsp;
|
||||
|
||||
out->hi[0] = mcontext.hi1;
|
||||
out->lo[0] = mcontext.lo1;
|
||||
out->hi[1] = mcontext.hi2;
|
||||
out->lo[1] = mcontext.lo2;
|
||||
out->hi[2] = mcontext.hi3;
|
||||
out->lo[2] = mcontext.lo3;
|
||||
|
||||
out->epc = mcontext.pc;
|
||||
out->badvaddr = 0; // Not stored in mcontext
|
||||
out->status = 0; // Not stored in mcontext
|
||||
out->cause = 0; // Not stored in mcontext
|
||||
|
||||
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||
out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs;
|
||||
|
||||
out->float_save.fpcsr = mcontext.fpc_csr;
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
out->float_save.fir = mcontext.fpc_eir;
|
||||
#endif
|
||||
}
|
||||
#endif // __mips__
|
||||
|
||||
void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
|
||||
assert(gp_regs || size);
|
||||
#if defined(__mips__)
|
||||
if (gp_regs)
|
||||
*gp_regs = mcontext.gregs;
|
||||
if (size)
|
||||
*size = sizeof(mcontext.gregs);
|
||||
#else
|
||||
if (gp_regs)
|
||||
*gp_regs = ®s;
|
||||
if (size)
|
||||
*size = sizeof(regs);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) {
|
||||
assert(fp_regs || size);
|
||||
#if defined(__mips__)
|
||||
if (fp_regs)
|
||||
*fp_regs = &mcontext.fpregs;
|
||||
if (size)
|
||||
*size = sizeof(mcontext.fpregs);
|
||||
#else
|
||||
if (fp_regs)
|
||||
*fp_regs = &fpregs;
|
||||
if (size)
|
||||
*size = sizeof(fpregs);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,91 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
||||
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
#endif
|
||||
|
||||
// We produce one of these structures for each thread in the crashed process.
|
||||
struct ThreadInfo {
|
||||
pid_t tgid; // thread group id
|
||||
pid_t ppid; // parent process
|
||||
|
||||
uintptr_t stack_pointer; // thread stack pointer
|
||||
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
user_regs_struct regs;
|
||||
user_fpregs_struct fpregs;
|
||||
static const unsigned kNumDebugRegisters = 8;
|
||||
debugreg_t dregs[8];
|
||||
#if defined(__i386)
|
||||
user_fpxregs_struct fpxregs;
|
||||
#endif // defined(__i386)
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
// Mimicking how strace does this(see syscall.c, search for GETREGS)
|
||||
struct user_regs regs;
|
||||
struct user_fpregs fpregs;
|
||||
#elif defined(__aarch64__)
|
||||
// Use the structures defined in <sys/user.h>
|
||||
struct user_regs_struct regs;
|
||||
struct user_fpsimd_struct fpregs;
|
||||
#elif defined(__mips__)
|
||||
// Use the structure defined in <sys/ucontext.h>.
|
||||
mcontext_t mcontext;
|
||||
#endif
|
||||
|
||||
// Returns the instruction pointer (platform-dependent impl.).
|
||||
uintptr_t GetInstructionPointer() const;
|
||||
|
||||
// Fills a RawContextCPU using the context in the ThreadInfo object.
|
||||
void FillCPUContext(RawContextCPU* out) const;
|
||||
|
||||
// Returns the pointer and size of general purpose register area.
|
||||
void GetGeneralPurposeRegisters(void** gp_regs, size_t* size);
|
||||
|
||||
// Returns the pointer and size of float point register area.
|
||||
void GetFloatingPointRegisters(void** fp_regs, size_t* size);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
@ -1,259 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Minidump defines register structures which are different from the raw
|
||||
// structures which we get from the kernel. These are platform specific
|
||||
// functions to juggle the ucontext and user structures into minidump format.
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_ESP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_EIP];
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fp) {
|
||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||
|
||||
out->context_flags = MD_CONTEXT_X86_FULL |
|
||||
MD_CONTEXT_X86_FLOATING_POINT;
|
||||
|
||||
out->gs = regs[REG_GS];
|
||||
out->fs = regs[REG_FS];
|
||||
out->es = regs[REG_ES];
|
||||
out->ds = regs[REG_DS];
|
||||
|
||||
out->edi = regs[REG_EDI];
|
||||
out->esi = regs[REG_ESI];
|
||||
out->ebx = regs[REG_EBX];
|
||||
out->edx = regs[REG_EDX];
|
||||
out->ecx = regs[REG_ECX];
|
||||
out->eax = regs[REG_EAX];
|
||||
|
||||
out->ebp = regs[REG_EBP];
|
||||
out->eip = regs[REG_EIP];
|
||||
out->cs = regs[REG_CS];
|
||||
out->eflags = regs[REG_EFL];
|
||||
out->esp = regs[REG_UESP];
|
||||
out->ss = regs[REG_SS];
|
||||
|
||||
out->float_save.control_word = fp->cw;
|
||||
out->float_save.status_word = fp->sw;
|
||||
out->float_save.tag_word = fp->tag;
|
||||
out->float_save.error_offset = fp->ipoff;
|
||||
out->float_save.error_selector = fp->cssel;
|
||||
out->float_save.data_offset = fp->dataoff;
|
||||
out->float_save.data_selector = fp->datasel;
|
||||
|
||||
// 8 registers * 10 bytes per register.
|
||||
my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
|
||||
}
|
||||
|
||||
#elif defined(__x86_64)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_RSP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_RIP];
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fpregs) {
|
||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||
|
||||
out->context_flags = MD_CONTEXT_AMD64_FULL;
|
||||
|
||||
out->cs = regs[REG_CSGSFS] & 0xffff;
|
||||
|
||||
out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
|
||||
out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
|
||||
|
||||
out->eflags = regs[REG_EFL];
|
||||
|
||||
out->rax = regs[REG_RAX];
|
||||
out->rcx = regs[REG_RCX];
|
||||
out->rdx = regs[REG_RDX];
|
||||
out->rbx = regs[REG_RBX];
|
||||
|
||||
out->rsp = regs[REG_RSP];
|
||||
out->rbp = regs[REG_RBP];
|
||||
out->rsi = regs[REG_RSI];
|
||||
out->rdi = regs[REG_RDI];
|
||||
out->r8 = regs[REG_R8];
|
||||
out->r9 = regs[REG_R9];
|
||||
out->r10 = regs[REG_R10];
|
||||
out->r11 = regs[REG_R11];
|
||||
out->r12 = regs[REG_R12];
|
||||
out->r13 = regs[REG_R13];
|
||||
out->r14 = regs[REG_R14];
|
||||
out->r15 = regs[REG_R15];
|
||||
|
||||
out->rip = regs[REG_RIP];
|
||||
|
||||
out->flt_save.control_word = fpregs->cwd;
|
||||
out->flt_save.status_word = fpregs->swd;
|
||||
out->flt_save.tag_word = fpregs->ftw;
|
||||
out->flt_save.error_opcode = fpregs->fop;
|
||||
out->flt_save.error_offset = fpregs->rip;
|
||||
out->flt_save.data_offset = fpregs->rdp;
|
||||
out->flt_save.error_selector = 0; // We don't have this.
|
||||
out->flt_save.data_selector = 0; // We don't have this.
|
||||
out->flt_save.mx_csr = fpregs->mxcsr;
|
||||
out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
|
||||
my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
|
||||
my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
||||
}
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.arm_sp;
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.arm_pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
||||
out->iregs[1] = uc->uc_mcontext.arm_r1;
|
||||
out->iregs[2] = uc->uc_mcontext.arm_r2;
|
||||
out->iregs[3] = uc->uc_mcontext.arm_r3;
|
||||
out->iregs[4] = uc->uc_mcontext.arm_r4;
|
||||
out->iregs[5] = uc->uc_mcontext.arm_r5;
|
||||
out->iregs[6] = uc->uc_mcontext.arm_r6;
|
||||
out->iregs[7] = uc->uc_mcontext.arm_r7;
|
||||
out->iregs[8] = uc->uc_mcontext.arm_r8;
|
||||
out->iregs[9] = uc->uc_mcontext.arm_r9;
|
||||
out->iregs[10] = uc->uc_mcontext.arm_r10;
|
||||
|
||||
out->iregs[11] = uc->uc_mcontext.arm_fp;
|
||||
out->iregs[12] = uc->uc_mcontext.arm_ip;
|
||||
out->iregs[13] = uc->uc_mcontext.arm_sp;
|
||||
out->iregs[14] = uc->uc_mcontext.arm_lr;
|
||||
out->iregs[15] = uc->uc_mcontext.arm_pc;
|
||||
|
||||
out->cpsr = uc->uc_mcontext.arm_cpsr;
|
||||
|
||||
// TODO: fix this after fixing ExceptionHandler
|
||||
out->float_save.fpscr = 0;
|
||||
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.sp;
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct fpsimd_context* fpregs) {
|
||||
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
|
||||
out->cpsr = static_cast<uint32_t>(uc->uc_mcontext.pstate);
|
||||
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
|
||||
out->iregs[i] = uc->uc_mcontext.regs[i];
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp;
|
||||
out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc;
|
||||
|
||||
out->float_save.fpsr = fpregs->fpsr;
|
||||
out->float_save.fpcr = fpregs->fpcr;
|
||||
my_memcpy(&out->float_save.regs, &fpregs->vregs,
|
||||
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
|
||||
}
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
return uc->uc_mcontext.pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
#else
|
||||
#error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = uc->uc_mcontext.gregs[i];
|
||||
|
||||
out->mdhi = uc->uc_mcontext.mdhi;
|
||||
out->mdlo = uc->uc_mcontext.mdlo;
|
||||
|
||||
out->hi[0] = uc->uc_mcontext.hi1;
|
||||
out->hi[1] = uc->uc_mcontext.hi2;
|
||||
out->hi[2] = uc->uc_mcontext.hi3;
|
||||
out->lo[0] = uc->uc_mcontext.lo1;
|
||||
out->lo[1] = uc->uc_mcontext.lo2;
|
||||
out->lo[2] = uc->uc_mcontext.lo3;
|
||||
out->dsp_control = uc->uc_mcontext.dsp;
|
||||
|
||||
out->epc = uc->uc_mcontext.pc;
|
||||
out->badvaddr = 0; // Not reported in signal context.
|
||||
out->status = 0; // Not reported in signal context.
|
||||
out->cause = 0; // Not reported in signal context.
|
||||
|
||||
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||
out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i];
|
||||
|
||||
out->float_save.fpcsr = uc->uc_mcontext.fpc_csr;
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,64 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
||||
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Wraps platform-dependent implementations of accessors to ucontext structs.
|
||||
struct UContextReader {
|
||||
static uintptr_t GetStackPointer(const struct ucontext* uc);
|
||||
|
||||
static uintptr_t GetInstructionPointer(const struct ucontext* uc);
|
||||
|
||||
// Juggle a arch-specific ucontext into a minidump format
|
||||
// out: the minidump structure
|
||||
// info: the collection of register structures.
|
||||
#if defined(__i386__) || defined(__x86_64)
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fp);
|
||||
#elif defined(__aarch64__)
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
const struct fpsimd_context* fpregs);
|
||||
#else
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
@ -68,38 +68,38 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#include <sys/signal.h>
|
||||
#endif
|
||||
#include <sys/syscall.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
#include <sys/wait.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/basictypes.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/memory.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "linux/sched.h"
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
@ -113,214 +113,144 @@ static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
// The list of signals which we consider to be crashes. The default action for
|
||||
// all these signals must be Core (see man 7 signal) because we rethrow the
|
||||
// signal after handling it and expect that it'll be fatal.
|
||||
const int kExceptionSignals[] = {
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP
|
||||
static const int kExceptionSignals[] = {
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
|
||||
};
|
||||
const int kNumHandledSignals =
|
||||
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
|
||||
struct sigaction old_handlers[kNumHandledSignals];
|
||||
bool handlers_installed = false;
|
||||
|
||||
// InstallAlternateStackLocked will store the newly installed stack in new_stack
|
||||
// and (if it exists) the previously installed stack in old_stack.
|
||||
stack_t old_stack;
|
||||
stack_t new_stack;
|
||||
bool stack_installed = false;
|
||||
|
||||
// Create an alternative stack to run the signal handlers on. This is done since
|
||||
// the signal might have been caused by a stack overflow.
|
||||
// Runs before crashing: normal context.
|
||||
void InstallAlternateStackLocked() {
|
||||
if (stack_installed)
|
||||
return;
|
||||
|
||||
memset(&old_stack, 0, sizeof(old_stack));
|
||||
memset(&new_stack, 0, sizeof(new_stack));
|
||||
|
||||
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
|
||||
// the alternative stack. Ensure that the size of the alternative stack is
|
||||
// large enough.
|
||||
static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ);
|
||||
|
||||
// Only set an alternative stack if there isn't already one, or if the current
|
||||
// one is too small.
|
||||
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
|
||||
old_stack.ss_size < kSigStackSize) {
|
||||
new_stack.ss_sp = calloc(1, kSigStackSize);
|
||||
new_stack.ss_size = kSigStackSize;
|
||||
|
||||
if (sys_sigaltstack(&new_stack, NULL) == -1) {
|
||||
free(new_stack.ss_sp);
|
||||
return;
|
||||
}
|
||||
stack_installed = true;
|
||||
}
|
||||
}
|
||||
// We can stack multiple exception handlers. In that case, this is the global
|
||||
// which holds the stack.
|
||||
std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||
unsigned ExceptionHandler::handler_stack_index_ = 0;
|
||||
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
|
||||
PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
void RestoreAlternateStackLocked() {
|
||||
if (!stack_installed)
|
||||
return;
|
||||
|
||||
stack_t current_stack;
|
||||
if (sys_sigaltstack(NULL, ¤t_stack) == -1)
|
||||
return;
|
||||
|
||||
// Only restore the old_stack if the current alternative stack is the one
|
||||
// installed by the call to InstallAlternateStackLocked.
|
||||
if (current_stack.ss_sp == new_stack.ss_sp) {
|
||||
if (old_stack.ss_sp) {
|
||||
if (sys_sigaltstack(&old_stack, NULL) == -1)
|
||||
return;
|
||||
} else {
|
||||
stack_t disable_stack;
|
||||
disable_stack.ss_flags = SS_DISABLE;
|
||||
if (sys_sigaltstack(&disable_stack, NULL) == -1)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
free(new_stack.ss_sp);
|
||||
stack_installed = false;
|
||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
handler_installed_(install_handler)
|
||||
{
|
||||
Init(dump_path, -1);
|
||||
}
|
||||
|
||||
void InstallDefaultHandler(int sig) {
|
||||
#if defined(__ANDROID__)
|
||||
// Android L+ expose signal and sigaction symbols that override the system
|
||||
// ones. There is a bug in these functions where a request to set the handler
|
||||
// to SIG_DFL is ignored. In that case, an infinite loop is entered as the
|
||||
// signal is repeatedly sent to breakpad's signal handler.
|
||||
// To work around this, directly call the system's sigaction.
|
||||
struct kernel_sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sys_sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler_ = SIG_DFL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t));
|
||||
#else
|
||||
signal(sig, SIG_DFL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The global exception handler stack. This is needed because there may exist
|
||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||
// registered in this stack.
|
||||
std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
|
||||
pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack
|
||||
// for SignalHandler(). Keep the crash context as a .bss field. Exception
|
||||
// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a
|
||||
// time can use |g_crash_context_|.
|
||||
ExceptionHandler::CrashContext g_crash_context_;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
minidump_descriptor_(descriptor),
|
||||
crash_handler_(NULL) {
|
||||
if (server_fd >= 0)
|
||||
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||
!minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
minidump_descriptor_.UpdatePath();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
logger::initializeCrashLogWriter();
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
|
||||
// Pre-fault the crash context struct. This is to avoid failing due to OOM
|
||||
// if handling an exception when the process ran out of virtual memory.
|
||||
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
|
||||
|
||||
if (!g_handler_stack_)
|
||||
g_handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||
if (install_handler) {
|
||||
InstallAlternateStackLocked();
|
||||
InstallHandlersLocked();
|
||||
}
|
||||
g_handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
handler_installed_(install_handler)
|
||||
{
|
||||
Init(dump_path, server_fd);
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::~ExceptionHandler() {
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this);
|
||||
g_handler_stack_->erase(handler);
|
||||
if (g_handler_stack_->empty()) {
|
||||
delete g_handler_stack_;
|
||||
g_handler_stack_ = NULL;
|
||||
RestoreAlternateStackLocked();
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
UninstallHandlers();
|
||||
}
|
||||
|
||||
void ExceptionHandler::Init(const std::string &dump_path,
|
||||
const int server_fd)
|
||||
{
|
||||
crash_handler_ = NULL;
|
||||
if (0 <= server_fd)
|
||||
crash_generation_client_
|
||||
.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
||||
if (handler_installed_)
|
||||
InstallHandlers();
|
||||
|
||||
if (!IsOutOfProcess())
|
||||
set_dump_path(dump_path);
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
if (handler_stack_ == NULL)
|
||||
handler_stack_ = new std::vector<ExceptionHandler *>;
|
||||
handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
// static
|
||||
bool ExceptionHandler::InstallHandlersLocked() {
|
||||
if (handlers_installed)
|
||||
return false;
|
||||
bool ExceptionHandler::InstallHandlers() {
|
||||
// We run the signal handlers on an alternative stack because we might have
|
||||
// crashed because of a stack overflow.
|
||||
|
||||
// Fail if unable to store all the old handlers.
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1)
|
||||
return false;
|
||||
}
|
||||
// We use this value rather than SIGSTKSZ because we would end up overrunning
|
||||
// such a small stack.
|
||||
static const unsigned kSigStackSize = 8192;
|
||||
|
||||
signal_stack = malloc(kSigStackSize);
|
||||
stack_t stack;
|
||||
memset(&stack, 0, sizeof(stack));
|
||||
stack.ss_sp = signal_stack;
|
||||
stack.ss_size = kSigStackSize;
|
||||
|
||||
if (sys_sigaltstack(&stack, NULL) == -1)
|
||||
return false;
|
||||
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
// Mask all exception signals when we're handling one of them.
|
||||
for (int i = 0; i < kNumHandledSignals; ++i)
|
||||
// mask all exception signals when we're handling one of them.
|
||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i)
|
||||
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
|
||||
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
|
||||
// At this point it is impractical to back out changes, and so failure to
|
||||
// install a signal is intentionally ignored.
|
||||
}
|
||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) {
|
||||
struct sigaction* old = new struct sigaction;
|
||||
if (sigaction(kExceptionSignals[i], &sa, old) == -1)
|
||||
return false;
|
||||
old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
|
||||
}
|
||||
handlers_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
void ExceptionHandler::RestoreHandlersLocked() {
|
||||
if (!handlers_installed)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
|
||||
InstallDefaultHandler(kExceptionSignals[i]);
|
||||
}
|
||||
// Runs before crashing: normal context.
|
||||
void ExceptionHandler::UninstallHandlers() {
|
||||
for (unsigned i = 0; i < old_handlers_.size(); ++i) {
|
||||
struct sigaction *action =
|
||||
reinterpret_cast<struct sigaction*>(old_handlers_[i].second);
|
||||
sigaction(old_handlers_[i].first, action, NULL);
|
||||
delete action;
|
||||
}
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(handler_stack_->begin(), handler_stack_->end(), this);
|
||||
handler_stack_->erase(handler);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
old_handlers_.clear();
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
void ExceptionHandler::UpdateNextID() {
|
||||
GUID guid;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
next_minidump_id_ = guid_str;
|
||||
next_minidump_id_c_ = next_minidump_id_.c_str();
|
||||
|
||||
char minidump_path[PATH_MAX];
|
||||
snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
|
||||
dump_path_c_,
|
||||
guid_str);
|
||||
|
||||
next_minidump_path_ = minidump_path;
|
||||
next_minidump_path_c_ = next_minidump_path_.c_str();
|
||||
}
|
||||
handlers_installed = false;
|
||||
}
|
||||
|
||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
||||
@ -332,61 +262,28 @@ void ExceptionHandler::RestoreHandlersLocked() {
|
||||
// static
|
||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// All the exception signals are blocked at this point.
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
|
||||
// Sometimes, Breakpad runs inside a process where some other buggy code
|
||||
// saves and restores signal handlers temporarily with 'signal'
|
||||
// instead of 'sigaction'. This loses the SA_SIGINFO flag associated
|
||||
// with this function. As a consequence, the values of 'info' and 'uc'
|
||||
// become totally bogus, generally inducing a crash.
|
||||
//
|
||||
// The following code tries to detect this case. When it does, it
|
||||
// resets the signal handlers with sigaction + SA_SIGINFO and returns.
|
||||
// This forces the signal to be thrown again, but this time the kernel
|
||||
// will call the function with the right arguments.
|
||||
struct sigaction cur_handler;
|
||||
if (sigaction(sig, NULL, &cur_handler) == 0 &&
|
||||
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
|
||||
// Reset signal handler with the right flags.
|
||||
sigemptyset(&cur_handler.sa_mask);
|
||||
sigaddset(&cur_handler.sa_mask, sig);
|
||||
|
||||
cur_handler.sa_sigaction = SignalHandler;
|
||||
cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
|
||||
if (sigaction(sig, &cur_handler, NULL) == -1) {
|
||||
// When resetting the handler fails, try to reset the
|
||||
// default one to avoid an infinite loop here.
|
||||
InstallDefaultHandler(sig);
|
||||
}
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
if (!handler_stack_->size()) {
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
return;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) {
|
||||
handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc);
|
||||
for (int i = handler_stack_->size() - 1; i >= 0; --i) {
|
||||
if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) {
|
||||
// successfully handled: We are in an invalid state since an exception
|
||||
// signal has been delivered. We don't call the exit handlers because
|
||||
// they could end up corrupting on-disk state.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Upon returning from this signal handler, sig will become unmasked and then
|
||||
// it will be retriggered. If one of the ExceptionHandlers handled it
|
||||
// successfully, restore the default handler. Otherwise, restore the
|
||||
// previously installed handler. Then, when the signal is retriggered, it will
|
||||
// be delivered to the appropriate handler.
|
||||
if (handled) {
|
||||
InstallDefaultHandler(sig);
|
||||
} else {
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
|
||||
// info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
|
||||
if (info->si_code <= 0 || sig == SIGABRT) {
|
||||
if (info->si_pid) {
|
||||
// This signal was triggered by somebody sending us the signal with kill().
|
||||
// In order to retrigger it, we have to queue a new signal by calling
|
||||
// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
|
||||
// due to the kernel sending a SIGABRT from a user request via SysRQ.
|
||||
// kill() ourselves.
|
||||
if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
|
||||
// If we failed to kill ourselves (e.g. because a sandbox disallows us
|
||||
// to do so), we instead resort to terminating our process. This will
|
||||
@ -398,11 +295,17 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// No need to reissue the signal. It will automatically trigger again,
|
||||
// when we return from the signal handler.
|
||||
}
|
||||
|
||||
// As soon as we return from the signal handler, our signal will become
|
||||
// unmasked. At that time, we will get terminated with the same signal that
|
||||
// was triggered originally. This allows our parent to know that we crashed.
|
||||
// The default action for all the signals which we catch is Core, so
|
||||
// this is the end of us.
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
||||
|
||||
struct ThreadArgument {
|
||||
pid_t pid; // the crashing process
|
||||
const MinidumpDescriptor* minidump_descriptor;
|
||||
ExceptionHandler* handler;
|
||||
const void* context; // a CrashContext structure
|
||||
size_t context_size;
|
||||
@ -433,52 +336,28 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
bool signal_pid_trusted = info->si_code == SI_USER ||
|
||||
info->si_code == SI_TKILL;
|
||||
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
|
||||
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
}
|
||||
|
||||
// Fill in all the holes in the struct to make Valgrind happy.
|
||||
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
|
||||
memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&g_crash_context_.context, uc, sizeof(struct ucontext));
|
||||
#if defined(__aarch64__)
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
struct fpsimd_context* fp_ptr =
|
||||
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
|
||||
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
|
||||
memcpy(&g_crash_context_.float_state, fp_ptr,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
}
|
||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
CrashContext context;
|
||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||
#if !defined(__ARM_EABI__)
|
||||
// FP state is not part of user ABI on ARM Linux.
|
||||
// In case of MIPS Linux FP state is already part of struct ucontext
|
||||
// and 'float_state' is not a member of CrashContext.
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
struct ucontext *uc_ptr = (struct ucontext*)uc;
|
||||
if (uc_ptr->uc_mcontext.fpregs) {
|
||||
memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
memcpy(&context.float_state,
|
||||
uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
}
|
||||
#endif
|
||||
g_crash_context_.tid = syscall(__NR_gettid);
|
||||
context.tid = syscall(__NR_gettid);
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_),
|
||||
if (crash_handler_(&context, sizeof(context),
|
||||
callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&g_crash_context_);
|
||||
}
|
||||
|
||||
// This is a public interface to HandleSignal that allows the client to
|
||||
// generate a crash dump. This function may run in a compromised context.
|
||||
bool ExceptionHandler::SimulateSignalDelivery(int sig) {
|
||||
siginfo_t siginfo = {};
|
||||
// Mimic a trusted signal to allow tracing the process (see
|
||||
// ExceptionHandler::HandleSignal().
|
||||
siginfo.si_code = SI_USER;
|
||||
siginfo.si_pid = getpid();
|
||||
struct ucontext context;
|
||||
getcontext(&context);
|
||||
return HandleSignal(sig, &siginfo, &context);
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
// This function may run in a compromised context: see the top of the file.
|
||||
@ -486,11 +365,9 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
if (IsOutOfProcess())
|
||||
return crash_generation_client_->RequestDump(context, sizeof(*context));
|
||||
|
||||
// Allocating too much stack isn't a problem, and better to err on the side
|
||||
// of caution than smash it into random locations.
|
||||
static const unsigned kChildStackSize = 16000;
|
||||
static const unsigned kChildStackSize = 8000;
|
||||
PageAllocator allocator;
|
||||
uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
|
||||
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
|
||||
if (!stack)
|
||||
return false;
|
||||
// clone() needs the top-most address. (scrub just to be safe)
|
||||
@ -499,7 +376,6 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
|
||||
ThreadArgument thread_arg;
|
||||
thread_arg.handler = this;
|
||||
thread_arg.minidump_descriptor = &minidump_descriptor_;
|
||||
thread_arg.pid = getpid();
|
||||
thread_arg.context = context;
|
||||
thread_arg.context_size = sizeof(*context);
|
||||
@ -508,48 +384,44 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
// kernels, but we need to know the PID of the cloned process before we
|
||||
// can do this. Create a pipe here which we can use to block the
|
||||
// cloned process after creating it, until we have explicitly enabled ptrace
|
||||
if (sys_pipe(fdes) == -1) {
|
||||
if(sys_pipe(fdes) == -1) {
|
||||
// Creating the pipe failed. We'll log an error but carry on anyway,
|
||||
// as we'll probably still get a useful crash report. All that will happen
|
||||
// is the write() and read() calls will fail with EBADF
|
||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump "
|
||||
"sys_pipe failed:";
|
||||
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
|
||||
// Ensure fdes[0] and fdes[1] are invalid file descriptors.
|
||||
fdes[0] = fdes[1] = -1;
|
||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
|
||||
sys_pipe failed:";
|
||||
sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
}
|
||||
|
||||
const pid_t child = sys_clone(
|
||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
&thread_arg, NULL, NULL, NULL);
|
||||
if (child == -1) {
|
||||
sys_close(fdes[0]);
|
||||
sys_close(fdes[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
int r, status;
|
||||
// Allow the child to ptrace us
|
||||
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
||||
prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
||||
SendContinueSignalToChild();
|
||||
int status;
|
||||
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
|
||||
do {
|
||||
r = sys_waitpid(child, &status, __WALL);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
sys_close(fdes[0]);
|
||||
sys_close(fdes[1]);
|
||||
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
sys_write(2, msg, sizeof(msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
}
|
||||
|
||||
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
|
||||
if (callback_)
|
||||
success = callback_(minidump_descriptor_, callback_context_, success);
|
||||
success = callback_(dump_path_c_, next_minidump_id_c_,
|
||||
callback_context_, success);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -558,12 +430,12 @@ void ExceptionHandler::SendContinueSignalToChild() {
|
||||
static const char okToContinueMessage = 'a';
|
||||
int r;
|
||||
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild "
|
||||
"sys_write failed:";
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
if(r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
|
||||
sys_write failed:";
|
||||
sys_write(2, msg, sizeof(msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,12 +445,12 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
int r;
|
||||
char receivedMessage;
|
||||
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal "
|
||||
"sys_read failed:";
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
if(r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
|
||||
sys_read failed:";
|
||||
sys_write(2, msg, sizeof(msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,127 +458,44 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
// Runs on the cloned process.
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
return google_breakpad::WriteMicrodump(
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
*minidump_descriptor_.microdump_extra_info());
|
||||
}
|
||||
if (minidump_descriptor_.IsFD()) {
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
}
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
return google_breakpad::WriteMinidump(next_minidump_path_c_,
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
mapping_list_);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidump(const string& dump_path,
|
||||
bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
MinidumpDescriptor descriptor(dump_path);
|
||||
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
|
||||
ExceptionHandler eh(dump_path, NULL, callback, callback_context, false);
|
||||
return eh.WriteMinidump();
|
||||
}
|
||||
|
||||
// In order to making using EBP to calculate the desired value for ESP
|
||||
// a valid operation, ensure that this function is compiled with a
|
||||
// frame pointer using the following attribute. This attribute
|
||||
// is supported on GCC but not on clang.
|
||||
#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__)
|
||||
__attribute__((optimize("no-omit-frame-pointer")))
|
||||
#endif
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||
!minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
// Update the path of the minidump so that this can be called multiple times
|
||||
// and new files are created for each minidump. This is done before the
|
||||
// generation happens, as clients may want to access the MinidumpDescriptor
|
||||
// after this call to find the exact path to the minidump file.
|
||||
minidump_descriptor_.UpdatePath();
|
||||
} else if (minidump_descriptor_.IsFD()) {
|
||||
// Reposition the FD to its beginning and resize it to get rid of the
|
||||
// previous minidump info.
|
||||
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
|
||||
ignore_result(ftruncate(minidump_descriptor_.fd(), 0));
|
||||
}
|
||||
|
||||
// Allow this process to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
#if !defined(__ARM_EABI__)
|
||||
// Allow ourselves to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
|
||||
CrashContext context;
|
||||
int getcontext_result = getcontext(&context.context);
|
||||
if (getcontext_result)
|
||||
return false;
|
||||
|
||||
#if defined(__i386__)
|
||||
// In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved
|
||||
// from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer
|
||||
// and it only makes sense when running in kernel mode with a different stack
|
||||
// pointer. When WriteMiniDump is called during normal processing REG_UESP is
|
||||
// zero which leads to bad minidump files.
|
||||
if (!context.context.uc_mcontext.gregs[REG_UESP]) {
|
||||
// If REG_UESP is set to REG_ESP then that includes the stack space for the
|
||||
// CrashContext object in this function, which is about 128 KB. Since the
|
||||
// Linux dumper only records 32 KB of stack this would mean that nothing
|
||||
// useful would be recorded. A better option is to set REG_UESP to REG_EBP,
|
||||
// perhaps with a small negative offset in case there is any code that
|
||||
// objects to them being equal.
|
||||
context.context.uc_mcontext.gregs[REG_UESP] =
|
||||
context.context.uc_mcontext.gregs[REG_EBP] - 16;
|
||||
// The stack saving is based off of REG_ESP so it must be set to match the
|
||||
// new REG_UESP.
|
||||
context.context.uc_mcontext.gregs[REG_ESP] =
|
||||
context.context.uc_mcontext.gregs[REG_UESP];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__)
|
||||
// FPU state is not part of ARM EABI ucontext_t.
|
||||
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
#endif
|
||||
context.tid = sys_gettid();
|
||||
|
||||
// Add an exception stream to the minidump for better reporting.
|
||||
memset(&context.siginfo, 0, sizeof(context.siginfo));
|
||||
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
|
||||
#if defined(__i386__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
|
||||
#elif defined(__x86_64__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
|
||||
#elif defined(__arm__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
|
||||
#elif defined(__aarch64__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
|
||||
#elif defined(__mips__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
|
||||
bool success = GenerateDump(&context);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
return GenerateDump(&context);
|
||||
return false;
|
||||
#endif // !defined(__ARM_EABI__)
|
||||
}
|
||||
|
||||
void ExceptionHandler::AddMappingInfo(const string& name,
|
||||
const uint8_t identifier[sizeof(MDGUID)],
|
||||
void ExceptionHandler::AddMappingInfo(const std::string& name,
|
||||
const u_int8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset) {
|
||||
@ -714,8 +503,7 @@ void ExceptionHandler::AddMappingInfo(const string& name,
|
||||
info.start_addr = start_address;
|
||||
info.size = mapping_size;
|
||||
info.offset = file_offset;
|
||||
strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
|
||||
info.name[sizeof(info.name) - 1] = '\0';
|
||||
strncpy(info.name, name.c_str(), std::min(name.size(), sizeof(info)));
|
||||
|
||||
MappingEntry mapping;
|
||||
mapping.first = info;
|
||||
@ -723,43 +511,4 @@ void ExceptionHandler::AddMappingInfo(const string& name,
|
||||
mapping_list_.push_back(mapping);
|
||||
}
|
||||
|
||||
void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
|
||||
AppMemoryList::iterator iter =
|
||||
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
|
||||
if (iter != app_memory_list_.end()) {
|
||||
// Don't allow registering the same pointer twice.
|
||||
return;
|
||||
}
|
||||
|
||||
AppMemory app_memory;
|
||||
app_memory.ptr = ptr;
|
||||
app_memory.length = length;
|
||||
app_memory_list_.push_back(app_memory);
|
||||
}
|
||||
|
||||
void ExceptionHandler::UnregisterAppMemory(void* ptr) {
|
||||
AppMemoryList::iterator iter =
|
||||
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
|
||||
if (iter != app_memory_list_.end()) {
|
||||
app_memory_list_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
|
||||
pid_t child_blamed_thread,
|
||||
const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
// This function is not run in a compromised context.
|
||||
MinidumpDescriptor descriptor(dump_path);
|
||||
descriptor.UpdatePath();
|
||||
if (!google_breakpad::WriteMinidump(descriptor.path(),
|
||||
child,
|
||||
child_blamed_thread))
|
||||
return false;
|
||||
|
||||
return callback ? callback(descriptor, callback_context, true) : true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -30,22 +30,28 @@
|
||||
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "client/linux/android_ucontext.h"
|
||||
#endif
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
struct sigaction;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
@ -64,18 +70,17 @@ namespace google_breakpad {
|
||||
// use different minidump callbacks for different call sites.
|
||||
//
|
||||
// In either case, a callback function is called when a minidump is written,
|
||||
// which receives the full path or file descriptor of the minidump. The
|
||||
// caller can collect and write additional application state to that minidump,
|
||||
// and launch an external crash-reporting application.
|
||||
// which receives the unqiue id of the minidump. The caller can use this
|
||||
// id to collect and write additional application state, and to launch an
|
||||
// external crash-reporting application.
|
||||
//
|
||||
// Caller should try to make the callbacks as crash-friendly as possible,
|
||||
// it should avoid use heap memory allocation as much as possible.
|
||||
|
||||
class ExceptionHandler {
|
||||
public:
|
||||
// A callback function to run before Breakpad performs any substantial
|
||||
// processing of an exception. A FilterCallback is called before writing
|
||||
// a minidump. |context| is the parameter supplied by the user as
|
||||
// a minidump. context is the parameter supplied by the user as
|
||||
// callback_context when the handler was created.
|
||||
//
|
||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
||||
@ -85,10 +90,10 @@ class ExceptionHandler {
|
||||
typedef bool (*FilterCallback)(void *context);
|
||||
|
||||
// A callback function to run after the minidump has been written.
|
||||
// |descriptor| contains the file descriptor or file path containing the
|
||||
// minidump. |context| is the parameter supplied by the user as
|
||||
// callback_context when the handler was created. |succeeded| indicates
|
||||
// whether a minidump file was successfully written.
|
||||
// minidump_id is a unique id for the dump, so the minidump
|
||||
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
|
||||
// by the user as callback_context when the handler was created. succeeded
|
||||
// indicates whether a minidump file was successfully written.
|
||||
//
|
||||
// If an exception occurred and the callback returns true, Breakpad will
|
||||
// treat the exception as fully-handled, suppressing any other handlers from
|
||||
@ -100,8 +105,9 @@ class ExceptionHandler {
|
||||
// should normally return the value of |succeeded|, or when they wish to
|
||||
// not report an exception of handled, false. Callbacks will rarely want to
|
||||
// return true directly (unless |succeeded| is true).
|
||||
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
typedef bool (*MinidumpCallback)(const char *dump_path,
|
||||
const char *minidump_id,
|
||||
void *context,
|
||||
bool succeeded);
|
||||
|
||||
// In certain cases, a user may wish to handle the generation of the minidump
|
||||
@ -114,77 +120,52 @@ class ExceptionHandler {
|
||||
void* context);
|
||||
|
||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
||||
// Before writing a minidump, the optional |filter| callback will be called.
|
||||
// Before writing a minidump, the optional filter callback will be called.
|
||||
// Its return value determines whether or not Breakpad should write a
|
||||
// minidump. The minidump content will be written to the file path or file
|
||||
// descriptor from |descriptor|, and the optional |callback| is called after
|
||||
// writing the dump file, as described above.
|
||||
// minidump. Minidump files will be written to dump_path, and the optional
|
||||
// callback is called after writing the dump file, as described above.
|
||||
// If install_handler is true, then a minidump will be written whenever
|
||||
// an unhandled exception occurs. If it is false, minidumps will only
|
||||
// be written when WriteMinidump is called.
|
||||
// If |server_fd| is valid, the minidump is generated out-of-process. If it
|
||||
// is -1, in-process generation will always be used.
|
||||
ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
ExceptionHandler(const std::string &dump_path,
|
||||
FilterCallback filter, MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler);
|
||||
|
||||
// Creates a new ExceptionHandler instance that can attempt to
|
||||
// perform out-of-process dump generation if server_fd is valid. If
|
||||
// server_fd is invalid, in-process dump generation will be
|
||||
// used. See the above ctor for a description of the other
|
||||
// parameters.
|
||||
ExceptionHandler(const std::string& dump_path,
|
||||
FilterCallback filter, MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd);
|
||||
|
||||
~ExceptionHandler();
|
||||
|
||||
const MinidumpDescriptor& minidump_descriptor() const {
|
||||
return minidump_descriptor_;
|
||||
}
|
||||
|
||||
void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
|
||||
minidump_descriptor_ = descriptor;
|
||||
// Get and set the minidump path.
|
||||
std::string dump_path() const { return dump_path_; }
|
||||
void set_dump_path(const std::string &dump_path) {
|
||||
dump_path_ = dump_path;
|
||||
dump_path_c_ = dump_path_.c_str();
|
||||
UpdateNextID();
|
||||
}
|
||||
|
||||
void set_crash_handler(HandlerCallback callback) {
|
||||
crash_handler_ = callback;
|
||||
}
|
||||
|
||||
void set_crash_generation_client(CrashGenerationClient* client) {
|
||||
crash_generation_client_.reset(client);
|
||||
}
|
||||
|
||||
// Writes a minidump immediately. This can be used to capture the execution
|
||||
// state independently of a crash.
|
||||
// Returns true on success.
|
||||
// If the ExceptionHandler has been created with a path, a new file is
|
||||
// generated for each minidump. The file path can be retrieved in the
|
||||
// MinidumpDescriptor passed to the MinidumpCallback or by accessing the
|
||||
// MinidumpDescriptor directly from the ExceptionHandler (with
|
||||
// minidump_descriptor()).
|
||||
// If the ExceptionHandler has been created with a file descriptor, the file
|
||||
// descriptor is repositioned to its beginning and the previous generated
|
||||
// minidump is overwritten.
|
||||
// Note that this method is not supposed to be called from a compromised
|
||||
// context as it uses the heap.
|
||||
// Writes a minidump immediately. This can be used to capture the
|
||||
// execution state independently of a crash. Returns true on success.
|
||||
bool WriteMinidump();
|
||||
|
||||
// Convenience form of WriteMinidump which does not require an
|
||||
// ExceptionHandler instance.
|
||||
static bool WriteMinidump(const string& dump_path,
|
||||
static bool WriteMinidump(const std::string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
// Write a minidump of |child| immediately. This can be used to
|
||||
// capture the execution state of |child| independently of a crash.
|
||||
// Pass a meaningful |child_blamed_thread| to make that thread in
|
||||
// the child process the one from which a crash signature is
|
||||
// extracted.
|
||||
//
|
||||
// WARNING: the return of this function *must* happen before
|
||||
// the code that will eventually reap |child| executes.
|
||||
// Otherwise there's a pernicious race condition in which |child|
|
||||
// exits, is reaped, another process created with its pid, then that
|
||||
// new process dumped.
|
||||
static bool WriteMinidumpForChild(pid_t child,
|
||||
pid_t child_blamed_thread,
|
||||
const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context);
|
||||
void *callback_context);
|
||||
|
||||
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
|
||||
// blob. It shouldn't be needed in any user code.
|
||||
@ -192,53 +173,39 @@ class ExceptionHandler {
|
||||
siginfo_t siginfo;
|
||||
pid_t tid; // the crashing thread.
|
||||
struct ucontext context;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
#if !defined(__ARM_EABI__)
|
||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
||||
// In case of MIPS Linux FP state is already part of struct
|
||||
// ucontext so 'float_state' is not required.
|
||||
fpstate_t float_state;
|
||||
struct _libc_fpstate float_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Returns whether out-of-process dump generation is used or not.
|
||||
bool IsOutOfProcess() const {
|
||||
return crash_generation_client_.get() != NULL;
|
||||
return crash_generation_client_.get() != NULL;
|
||||
}
|
||||
|
||||
// Add information about a memory mapping. This can be used if
|
||||
// a custom library loader is used that maps things in a way
|
||||
// that the linux dumper can't handle by reading the maps file.
|
||||
void AddMappingInfo(const string& name,
|
||||
const uint8_t identifier[sizeof(MDGUID)],
|
||||
void AddMappingInfo(const std::string& name,
|
||||
const u_int8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset);
|
||||
|
||||
// Register a block of memory of length bytes starting at address ptr
|
||||
// to be copied to the minidump when a crash happens.
|
||||
void RegisterAppMemory(void* ptr, size_t length);
|
||||
|
||||
// Unregister a block of memory that was registered with RegisterAppMemory.
|
||||
void UnregisterAppMemory(void* ptr);
|
||||
|
||||
// Force signal handling for the specified signal.
|
||||
bool SimulateSignalDelivery(int sig);
|
||||
|
||||
// Report a crash signal from an SA_SIGINFO signal handler.
|
||||
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
||||
|
||||
private:
|
||||
// Save the old signal handlers and install new ones.
|
||||
static bool InstallHandlersLocked();
|
||||
// Restore the old signal handlers.
|
||||
static void RestoreHandlersLocked();
|
||||
|
||||
void Init(const std::string &dump_path,
|
||||
const int server_fd);
|
||||
bool InstallHandlers();
|
||||
void UninstallHandlers();
|
||||
void PreresolveSymbols();
|
||||
bool GenerateDump(CrashContext *context);
|
||||
void SendContinueSignalToChild();
|
||||
void WaitForContinueSignal();
|
||||
|
||||
void UpdateNextID();
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
||||
static int ThreadEntry(void* arg);
|
||||
bool DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size);
|
||||
@ -249,28 +216,42 @@ class ExceptionHandler {
|
||||
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
|
||||
MinidumpDescriptor minidump_descriptor_;
|
||||
std::string dump_path_;
|
||||
std::string next_minidump_path_;
|
||||
std::string next_minidump_id_;
|
||||
|
||||
// Must be volatile. The compiler is unaware of the code which runs in
|
||||
// the signal handler which reads this variable. Without volatile the
|
||||
// compiler is free to optimise away writes to this variable which it
|
||||
// believes are never read.
|
||||
volatile HandlerCallback crash_handler_;
|
||||
// Pointers to C-string representations of the above. These are set
|
||||
// when the above are set so we can avoid calling c_str during
|
||||
// an exception.
|
||||
const char* dump_path_c_;
|
||||
const char* next_minidump_path_c_;
|
||||
const char* next_minidump_id_c_;
|
||||
|
||||
const bool handler_installed_;
|
||||
void* signal_stack; // the handler stack.
|
||||
HandlerCallback crash_handler_;
|
||||
|
||||
// The global exception handler stack. This is need becuase there may exist
|
||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||
// registered in this stack.
|
||||
static std::vector<ExceptionHandler*> *handler_stack_;
|
||||
// The index of the handler that should handle the next exception.
|
||||
static unsigned handler_stack_index_;
|
||||
static pthread_mutex_t handler_stack_mutex_;
|
||||
|
||||
// A vector of the old signal handlers.
|
||||
std::vector<std::pair<int, struct sigaction *> > old_handlers_;
|
||||
|
||||
// We need to explicitly enable ptrace of parent processes on some
|
||||
// kernels, but we need to know the PID of the cloned process before we
|
||||
// can do this. We create a pipe which we can use to block the
|
||||
// cloned process after creating it, until we have explicitly enabled
|
||||
// cloned process after creating it, until we have explicitly enabled
|
||||
// ptrace. This is used to store the file descriptors for the pipe
|
||||
int fdes[2];
|
||||
|
||||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
MappingList mapping_list_;
|
||||
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
AppMemoryList app_memory_list_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
// Copyright 2015 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
||||
#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct MicrodumpExtraInfo {
|
||||
// Strings pointed to by this struct are not copied, and are
|
||||
// expected to remain valid for the lifetime of the process.
|
||||
const char* build_fingerprint;
|
||||
const char* product_info;
|
||||
const char* gpu_fingerprint;
|
||||
|
||||
MicrodumpExtraInfo()
|
||||
: build_fingerprint(NULL), product_info(NULL), gpu_fingerprint(NULL) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
@ -1,87 +0,0 @@
|
||||
// Copyright (c) 2012 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//static
|
||||
const MinidumpDescriptor::MicrodumpOnConsole
|
||||
MinidumpDescriptor::kMicrodumpOnConsole = {};
|
||||
|
||||
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||
: mode_(descriptor.mode_),
|
||||
fd_(descriptor.fd_),
|
||||
directory_(descriptor.directory_),
|
||||
c_path_(NULL),
|
||||
size_limit_(descriptor.size_limit_),
|
||||
microdump_extra_info_(descriptor.microdump_extra_info_) {
|
||||
// The copy constructor is not allowed to be called on a MinidumpDescriptor
|
||||
// with a valid path_, as getting its c_path_ would require the heap which
|
||||
// can cause problems in compromised environments.
|
||||
assert(descriptor.path_.empty());
|
||||
}
|
||||
|
||||
MinidumpDescriptor& MinidumpDescriptor::operator=(
|
||||
const MinidumpDescriptor& descriptor) {
|
||||
assert(descriptor.path_.empty());
|
||||
|
||||
mode_ = descriptor.mode_;
|
||||
fd_ = descriptor.fd_;
|
||||
directory_ = descriptor.directory_;
|
||||
path_.clear();
|
||||
if (c_path_) {
|
||||
// This descriptor already had a path set, so generate a new one.
|
||||
c_path_ = NULL;
|
||||
UpdatePath();
|
||||
}
|
||||
size_limit_ = descriptor.size_limit_;
|
||||
microdump_extra_info_ = descriptor.microdump_extra_info_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MinidumpDescriptor::UpdatePath() {
|
||||
assert(mode_ == kWriteMinidumpToFile && !directory_.empty());
|
||||
|
||||
GUID guid;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
path_.clear();
|
||||
path_ = directory_ + "/" + guid_str + ".dmp";
|
||||
c_path_ = path_.c_str();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,149 +0,0 @@
|
||||
// Copyright (c) 2012 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
||||
#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// This class describes how a crash dump should be generated, either:
|
||||
// - Writing a full minidump to a file in a given directory (the actual path,
|
||||
// inside the directory, is determined by this class).
|
||||
// - Writing a full minidump to a given fd.
|
||||
// - Writing a reduced microdump to the console (logcat on Android).
|
||||
namespace google_breakpad {
|
||||
|
||||
class MinidumpDescriptor {
|
||||
public:
|
||||
struct MicrodumpOnConsole {};
|
||||
static const MicrodumpOnConsole kMicrodumpOnConsole;
|
||||
|
||||
MinidumpDescriptor()
|
||||
: mode_(kUninitialized),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
|
||||
explicit MinidumpDescriptor(const string& directory)
|
||||
: mode_(kWriteMinidumpToFile),
|
||||
fd_(-1),
|
||||
directory_(directory),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
assert(!directory.empty());
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(int fd)
|
||||
: mode_(kWriteMinidumpToFd),
|
||||
fd_(fd),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
assert(fd != -1);
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
|
||||
: mode_(kWriteMicrodumpToConsole),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
|
||||
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
||||
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
||||
|
||||
static MinidumpDescriptor getMicrodumpDescriptor();
|
||||
|
||||
bool IsFD() const { return mode_ == kWriteMinidumpToFd; }
|
||||
|
||||
int fd() const { return fd_; }
|
||||
|
||||
string directory() const { return directory_; }
|
||||
|
||||
const char* path() const { return c_path_; }
|
||||
|
||||
bool IsMicrodumpOnConsole() const {
|
||||
return mode_ == kWriteMicrodumpToConsole;
|
||||
}
|
||||
|
||||
// Updates the path so it is unique.
|
||||
// Should be called from a normal context: this methods uses the heap.
|
||||
void UpdatePath();
|
||||
|
||||
off_t size_limit() const { return size_limit_; }
|
||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||
|
||||
MicrodumpExtraInfo* microdump_extra_info() {
|
||||
assert(IsMicrodumpOnConsole());
|
||||
return µdump_extra_info_;
|
||||
};
|
||||
|
||||
private:
|
||||
enum DumpMode {
|
||||
kUninitialized = 0,
|
||||
kWriteMinidumpToFile,
|
||||
kWriteMinidumpToFd,
|
||||
kWriteMicrodumpToConsole
|
||||
};
|
||||
|
||||
// Specifies the dump mode (see DumpMode).
|
||||
DumpMode mode_;
|
||||
|
||||
// The file descriptor where the minidump is generated.
|
||||
int fd_;
|
||||
|
||||
// The directory where the minidump should be generated.
|
||||
string directory_;
|
||||
|
||||
// The full path to the generated minidump.
|
||||
string path_;
|
||||
|
||||
// The C string of |path_|. Precomputed so it can be access from a compromised
|
||||
// context.
|
||||
const char* c_path_;
|
||||
|
||||
off_t size_limit_;
|
||||
|
||||
// The extra microdump data (e.g. product name/version, build
|
||||
// fingerprint, gpu fingerprint) that should be appended to the dump
|
||||
// (microdump only). Microdumps don't have the ability of appending
|
||||
// extra metadata after the dump is generated (as opposite to
|
||||
// minidumps MIME fields), therefore the extra data must be provided
|
||||
// upfront. Any memory pointed to by members of the
|
||||
// MicrodumpExtraInfo struct must be valid for the lifetime of the
|
||||
// process (read: the caller has to guarantee that it is stored in
|
||||
// global static storage.)
|
||||
MicrodumpExtraInfo microdump_extra_info_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
@ -1,84 +0,0 @@
|
||||
// Copyright (c) 2012 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "client/linux/log/log.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
#else
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
namespace logger {
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
namespace {
|
||||
|
||||
// __android_log_buf_write() is not exported in the NDK and is being used by
|
||||
// dynamic runtime linking. Its declaration is taken from Android's
|
||||
// system/core/include/log/log.h.
|
||||
using AndroidLogBufferWriteFunc = int (*)(int bufID, int prio, const char *tag,
|
||||
const char *text);
|
||||
const int kAndroidCrashLogId = 4; // From LOG_ID_CRASH in log.h.
|
||||
const char kAndroidLogTag[] = "google-breakpad";
|
||||
|
||||
bool g_crash_log_initialized = false;
|
||||
AndroidLogBufferWriteFunc g_android_log_buf_write = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
void initializeCrashLogWriter() {
|
||||
if (g_crash_log_initialized)
|
||||
return;
|
||||
g_android_log_buf_write = reinterpret_cast<AndroidLogBufferWriteFunc>(
|
||||
dlsym(RTLD_DEFAULT, "__android_log_buf_write"));
|
||||
g_crash_log_initialized = true;
|
||||
}
|
||||
|
||||
int writeToCrashLog(const char* buf) {
|
||||
// Try writing to the crash log ring buffer. If not available, fall back to
|
||||
// the standard log buffer.
|
||||
if (g_android_log_buf_write) {
|
||||
return g_android_log_buf_write(kAndroidCrashLogId, ANDROID_LOG_FATAL,
|
||||
kAndroidLogTag, buf);
|
||||
}
|
||||
return __android_log_write(ANDROID_LOG_FATAL, kAndroidLogTag, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
int write(const char* buf, size_t nbytes) {
|
||||
#if defined(__ANDROID__)
|
||||
return __android_log_write(ANDROID_LOG_WARN, kAndroidLogTag, buf);
|
||||
#else
|
||||
return sys_write(2, buf, nbytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace logger
|
@ -1,55 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_LOG_LOG_H_
|
||||
#define CLIENT_LINUX_LOG_LOG_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace logger {
|
||||
|
||||
int write(const char* buf, size_t nbytes);
|
||||
|
||||
// In the case of Android the log can be written to the default system log
|
||||
// (default behavior of write() above, or to the crash log (see
|
||||
// writeToCrashLog() below).
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
// The logger must be initialized in a non-compromised context.
|
||||
void initializeCrashLogWriter();
|
||||
|
||||
// Once initialized, writeToCrashLog is safe to use in a compromised context,
|
||||
// even if the initialization failed, in which case this will silently fall
|
||||
// back on write().
|
||||
int writeToCrashLog(const char* buf);
|
||||
#endif
|
||||
|
||||
} // namespace logger
|
||||
|
||||
#endif // CLIENT_LINUX_LOG_LOG_H_
|
@ -1,598 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This translation unit generates microdumps into the console (logcat on
|
||||
// Android). See crbug.com/410294 for more info and design docs.
|
||||
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/memory.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::auto_wasteful_vector;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::kDefaultBuildIdSize;
|
||||
using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
using google_breakpad::MappingInfo;
|
||||
using google_breakpad::MappingList;
|
||||
using google_breakpad::MicrodumpExtraInfo;
|
||||
using google_breakpad::RawContextCPU;
|
||||
using google_breakpad::ThreadInfo;
|
||||
using google_breakpad::UContextReader;
|
||||
|
||||
const size_t kLineBufferSize = 2048;
|
||||
|
||||
#if !defined(__LP64__)
|
||||
// The following are only used by DumpFreeSpace, so need to be compiled
|
||||
// in conditionally in the same way.
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
Dst saturated_cast(Src src) {
|
||||
if (src >= std::numeric_limits<Dst>::max())
|
||||
return std::numeric_limits<Dst>::max();
|
||||
if (src <= std::numeric_limits<Dst>::min())
|
||||
return std::numeric_limits<Dst>::min();
|
||||
return static_cast<Dst>(src);
|
||||
}
|
||||
|
||||
int Log2Floor(uint64_t n) {
|
||||
// Copied from chromium src/base/bits.h
|
||||
if (n == 0)
|
||||
return -1;
|
||||
int log = 0;
|
||||
uint64_t value = n;
|
||||
for (int i = 5; i >= 0; --i) {
|
||||
int shift = (1 << i);
|
||||
uint64_t x = value >> shift;
|
||||
if (x != 0) {
|
||||
value = x;
|
||||
log += shift;
|
||||
}
|
||||
}
|
||||
assert(value == 1u);
|
||||
return log;
|
||||
}
|
||||
|
||||
bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) {
|
||||
// Because of load biasing, we can end up with a situation where two
|
||||
// mappings actually overlap. So we will define adjacency to also include a
|
||||
// b start address that lies within a's address range (including starting
|
||||
// immediately after a).
|
||||
// Because load biasing only ever moves the start address backwards, the end
|
||||
// address should still increase.
|
||||
return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr;
|
||||
}
|
||||
|
||||
bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) {
|
||||
// Return true if mapping a is before mapping b.
|
||||
// For the same reason (load biasing) we compare end addresses, which - unlike
|
||||
// start addresses - will not have been modified.
|
||||
return a->start_addr + a->size < b->start_addr + b->size;
|
||||
}
|
||||
|
||||
size_t NextOrderedMapping(
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings,
|
||||
size_t curr) {
|
||||
// Find the mapping that directly follows mappings[curr].
|
||||
// If no such mapping exists, return |invalid| to indicate this.
|
||||
const size_t invalid = std::numeric_limits<size_t>::max();
|
||||
size_t best = invalid;
|
||||
for (size_t next = 0; next < mappings.size(); ++next) {
|
||||
if (MappingLessThan(mappings[curr], mappings[next]) &&
|
||||
(best == invalid || MappingLessThan(mappings[next], mappings[best]))) {
|
||||
best = next;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
#endif // !__LP64__
|
||||
|
||||
class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
LinuxDumper* dumper)
|
||||
: ucontext_(context ? &context->context : NULL),
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
float_state_(context ? &context->float_state : NULL),
|
||||
#endif
|
||||
dumper_(dumper),
|
||||
mapping_list_(mappings),
|
||||
microdump_extra_info_(microdump_extra_info),
|
||||
log_line_(NULL) {
|
||||
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
|
||||
if (log_line_)
|
||||
log_line_[0] = '\0'; // Clear out the log line buffer.
|
||||
}
|
||||
|
||||
~MicrodumpWriter() { dumper_->ThreadsResume(); }
|
||||
|
||||
bool Init() {
|
||||
// In the exceptional case where the system was out of memory and there
|
||||
// wasn't even room to allocate the line buffer, bail out. There is nothing
|
||||
// useful we can possibly achieve without the ability to Log. At least let's
|
||||
// try to not crash.
|
||||
if (!dumper_->Init() || !log_line_)
|
||||
return false;
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
bool success;
|
||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
DumpGPUInformation();
|
||||
#if !defined(__LP64__)
|
||||
DumpFreeSpace();
|
||||
#endif
|
||||
success = DumpCrashingThread();
|
||||
if (success)
|
||||
success = DumpMappings();
|
||||
LogLine("-----END BREAKPAD MICRODUMP-----");
|
||||
dumper_->ThreadsResume();
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
// Writes one line to the system log.
|
||||
void LogLine(const char* msg) {
|
||||
#if defined(__ANDROID__)
|
||||
logger::writeToCrashLog(msg);
|
||||
#else
|
||||
logger::write(msg, my_strlen(msg));
|
||||
logger::write("\n", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Stages the given string in the current line buffer.
|
||||
void LogAppend(const char* str) {
|
||||
my_strlcat(log_line_, str, kLineBufferSize);
|
||||
}
|
||||
|
||||
// As above (required to take precedence over template specialization below).
|
||||
void LogAppend(char* str) {
|
||||
LogAppend(const_cast<const char*>(str));
|
||||
}
|
||||
|
||||
// Stages the hex repr. of the given int type in the current line buffer.
|
||||
template<typename T>
|
||||
void LogAppend(T value) {
|
||||
// Make enough room to hex encode the largest int type + NUL.
|
||||
static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
char hexstr[sizeof(T) * 2 + 1];
|
||||
for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
|
||||
hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
|
||||
hexstr[sizeof(T) * 2] = '\0';
|
||||
LogAppend(hexstr);
|
||||
}
|
||||
|
||||
// Stages the buffer content hex-encoded in the current line buffer.
|
||||
void LogAppend(const void* buf, size_t length) {
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
|
||||
for (size_t i = 0; i < length; ++i, ++ptr)
|
||||
LogAppend(*ptr);
|
||||
}
|
||||
|
||||
// Writes out the current line buffer on the system log.
|
||||
void LogCommitLine() {
|
||||
LogLine(log_line_);
|
||||
my_strlcpy(log_line_, "", kLineBufferSize);
|
||||
}
|
||||
|
||||
void DumpProductInformation() {
|
||||
LogAppend("V ");
|
||||
if (microdump_extra_info_.product_info) {
|
||||
LogAppend(microdump_extra_info_.product_info);
|
||||
} else {
|
||||
LogAppend("UNKNOWN:0.0.0.0");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpOSInformation() {
|
||||
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
const char kOSId[] = "A";
|
||||
#else
|
||||
const char kOSId[] = "L";
|
||||
#endif
|
||||
|
||||
// Dump the runtime architecture. On multiarch devices it might not match the
|
||||
// hw architecture (the one returned by uname()), for instance in the case of
|
||||
// a 32-bit app running on a aarch64 device.
|
||||
#if defined(__aarch64__)
|
||||
const char kArch[] = "arm64";
|
||||
#elif defined(__ARMEL__)
|
||||
const char kArch[] = "arm";
|
||||
#elif defined(__x86_64__)
|
||||
const char kArch[] = "x86_64";
|
||||
#elif defined(__i386__)
|
||||
const char kArch[] = "x86";
|
||||
#elif defined(__mips__)
|
||||
# if _MIPS_SIM == _ABIO32
|
||||
const char kArch[] = "mips";
|
||||
# elif _MIPS_SIM == _ABI64
|
||||
const char kArch[] = "mips64";
|
||||
# else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet"
|
||||
#endif
|
||||
|
||||
LogAppend("O ");
|
||||
LogAppend(kOSId);
|
||||
LogAppend(" ");
|
||||
LogAppend(kArch);
|
||||
LogAppend(" ");
|
||||
LogAppend(n_cpus);
|
||||
LogAppend(" ");
|
||||
|
||||
// Dump the HW architecture (e.g., armv7l, aarch64).
|
||||
struct utsname uts;
|
||||
const bool has_uts_info = (uname(&uts) == 0);
|
||||
const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch";
|
||||
LogAppend(hwArch);
|
||||
LogAppend(" ");
|
||||
|
||||
// If the client has attached a build fingerprint to the MinidumpDescriptor
|
||||
// use that one. Otherwise try to get some basic info from uname().
|
||||
if (microdump_extra_info_.build_fingerprint) {
|
||||
LogAppend(microdump_extra_info_.build_fingerprint);
|
||||
} else if (has_uts_info) {
|
||||
LogAppend(uts.release);
|
||||
LogAppend(" ");
|
||||
LogAppend(uts.version);
|
||||
} else {
|
||||
LogAppend("no build fingerprint available");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpGPUInformation() {
|
||||
LogAppend("G ");
|
||||
if (microdump_extra_info_.gpu_fingerprint) {
|
||||
LogAppend(microdump_extra_info_.gpu_fingerprint);
|
||||
} else {
|
||||
LogAppend("UNKNOWN");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
bool DumpThreadStack(uint32_t thread_id,
|
||||
uintptr_t stack_pointer,
|
||||
int max_stack_len,
|
||||
uint8_t** stack_copy) {
|
||||
*stack_copy = NULL;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
// The stack pointer might not be available. In this case we don't hard
|
||||
// fail, just produce a (almost useless) microdump w/o a stack section.
|
||||
return true;
|
||||
}
|
||||
|
||||
LogAppend("S 0 ");
|
||||
LogAppend(stack_pointer);
|
||||
LogAppend(" ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack));
|
||||
LogAppend(" ");
|
||||
LogAppend(stack_len);
|
||||
LogCommitLine();
|
||||
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
}
|
||||
|
||||
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
|
||||
|
||||
// Dump the content of the stack, splicing it into chunks which size is
|
||||
// compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
|
||||
const size_t STACK_DUMP_CHUNK_SIZE = 384;
|
||||
for (size_t stack_off = 0; stack_off < stack_len;
|
||||
stack_off += STACK_DUMP_CHUNK_SIZE) {
|
||||
LogAppend("S ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
|
||||
LogAppend(" ");
|
||||
LogAppend(*stack_copy + stack_off,
|
||||
std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
|
||||
LogCommitLine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write information about the crashing thread.
|
||||
bool DumpCrashingThread() {
|
||||
const unsigned num_threads = dumper_->threads().size();
|
||||
|
||||
for (unsigned i = 0; i < num_threads; ++i) {
|
||||
MDRawThread thread;
|
||||
my_memset(&thread, 0, sizeof(thread));
|
||||
thread.thread_id = dumper_->threads()[i];
|
||||
|
||||
// Dump only the crashing thread.
|
||||
if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
|
||||
continue;
|
||||
|
||||
assert(ucontext_);
|
||||
assert(!dumper_->IsPostMortem());
|
||||
|
||||
uint8_t* stack_copy;
|
||||
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||
if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
|
||||
return false;
|
||||
|
||||
RawContextCPU cpu;
|
||||
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||
#else
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
#endif
|
||||
DumpCPUState(&cpu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpCPUState(RawContextCPU* cpu) {
|
||||
LogAppend("C ");
|
||||
LogAppend(cpu, sizeof(*cpu));
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
// If there is caller-provided information about this mapping
|
||||
// in the mapping_list_ list, return true. Otherwise, return false.
|
||||
bool HaveMappingInfo(const MappingInfo& mapping) {
|
||||
for (MappingList::const_iterator iter = mapping_list_.begin();
|
||||
iter != mapping_list_.end();
|
||||
++iter) {
|
||||
// Ignore any mappings that are wholly contained within
|
||||
// mappings in the mapping_info_ list.
|
||||
if (mapping.start_addr >= iter->first.start_addr &&
|
||||
(mapping.start_addr + mapping.size) <=
|
||||
(iter->first.start_addr + iter->first.size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dump information about the provided |mapping|. If |identifier| is non-NULL,
|
||||
// use it instead of calculating a file ID from the mapping.
|
||||
void DumpModule(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
const uint8_t* identifier) {
|
||||
|
||||
auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
|
||||
dumper_->allocator());
|
||||
|
||||
if (identifier) {
|
||||
// GUID was provided by caller.
|
||||
identifier_bytes.insert(identifier_bytes.end(),
|
||||
identifier,
|
||||
identifier + sizeof(MDGUID));
|
||||
} else {
|
||||
dumper_->ElfFileIdentifierForMapping(
|
||||
mapping,
|
||||
member,
|
||||
mapping_id,
|
||||
identifier_bytes);
|
||||
}
|
||||
|
||||
// Copy as many bytes of |identifier| as will fit into a MDGUID
|
||||
MDGUID module_identifier = {0};
|
||||
memcpy(&module_identifier, &identifier_bytes[0],
|
||||
std::min(sizeof(MDGUID), identifier_bytes.size()));
|
||||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
dumper_->GetMappingEffectiveNameAndPath(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||
|
||||
LogAppend("M ");
|
||||
LogAppend(static_cast<uintptr_t>(mapping.start_addr));
|
||||
LogAppend(" ");
|
||||
LogAppend(mapping.offset);
|
||||
LogAppend(" ");
|
||||
LogAppend(mapping.size);
|
||||
LogAppend(" ");
|
||||
LogAppend(module_identifier.data1);
|
||||
LogAppend(module_identifier.data2);
|
||||
LogAppend(module_identifier.data3);
|
||||
LogAppend(module_identifier.data4[0]);
|
||||
LogAppend(module_identifier.data4[1]);
|
||||
LogAppend(module_identifier.data4[2]);
|
||||
LogAppend(module_identifier.data4[3]);
|
||||
LogAppend(module_identifier.data4[4]);
|
||||
LogAppend(module_identifier.data4[5]);
|
||||
LogAppend(module_identifier.data4[6]);
|
||||
LogAppend(module_identifier.data4[7]);
|
||||
LogAppend("0 "); // Age is always 0 on Linux.
|
||||
LogAppend(file_name);
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
#if !defined(__LP64__)
|
||||
void DumpFreeSpace() {
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
|
||||
dumper_->mappings();
|
||||
if (mappings.size() == 0) return;
|
||||
|
||||
// This is complicated by the fact that mappings is not in order. It should
|
||||
// be mostly in order, however the mapping that contains the entry point for
|
||||
// the process is always at the front of the vector.
|
||||
|
||||
static const int HBITS = sizeof(size_t) * 8;
|
||||
size_t hole_histogram[HBITS];
|
||||
my_memset(hole_histogram, 0, sizeof(hole_histogram));
|
||||
|
||||
// Find the lowest address mapping.
|
||||
size_t curr = 0;
|
||||
for (size_t i = 1; i < mappings.size(); ++i) {
|
||||
if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i;
|
||||
}
|
||||
|
||||
uintptr_t lo_addr = mappings[curr]->start_addr;
|
||||
|
||||
size_t hole_cnt = 0;
|
||||
size_t hole_max = 0;
|
||||
size_t hole_sum = 0;
|
||||
|
||||
while (true) {
|
||||
// Skip to the end of an adjacent run of mappings. This is an optimization
|
||||
// for the fact that mappings is mostly sorted.
|
||||
while (curr != mappings.size() - 1 &&
|
||||
MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) {
|
||||
++curr;
|
||||
}
|
||||
|
||||
size_t next = NextOrderedMapping(mappings, curr);
|
||||
if (next == std::numeric_limits<size_t>::max())
|
||||
break;
|
||||
|
||||
uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size;
|
||||
uintptr_t hole_hi = mappings[next]->start_addr;
|
||||
|
||||
if (hole_hi > hole_lo) {
|
||||
size_t hole_sz = hole_hi - hole_lo;
|
||||
hole_sum += hole_sz;
|
||||
hole_max = std::max(hole_sz, hole_max);
|
||||
++hole_cnt;
|
||||
++hole_histogram[Log2Floor(hole_sz)];
|
||||
}
|
||||
curr = next;
|
||||
}
|
||||
|
||||
uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size;
|
||||
|
||||
LogAppend("H ");
|
||||
LogAppend(lo_addr);
|
||||
LogAppend(" ");
|
||||
LogAppend(hi_addr);
|
||||
LogAppend(" ");
|
||||
LogAppend(saturated_cast<uint16_t>(hole_cnt));
|
||||
LogAppend(" ");
|
||||
LogAppend(hole_max);
|
||||
LogAppend(" ");
|
||||
LogAppend(hole_sum);
|
||||
for (unsigned int i = 0; i < HBITS; ++i) {
|
||||
if (!hole_histogram[i]) continue;
|
||||
LogAppend(" ");
|
||||
LogAppend(saturated_cast<uint8_t>(i));
|
||||
LogAppend(":");
|
||||
LogAppend(saturated_cast<uint8_t>(hole_histogram[i]));
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write information about the mappings in effect.
|
||||
bool DumpMappings() {
|
||||
// First write all the mappings from the dumper
|
||||
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper_->mappings()[i];
|
||||
if (mapping.name[0] == 0 || // only want modules with filenames.
|
||||
!mapping.exec || // only want executable mappings.
|
||||
mapping.size < 4096 || // too small to get a signature for.
|
||||
HaveMappingInfo(mapping)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DumpModule(mapping, true, i, NULL);
|
||||
}
|
||||
// Next write all the mappings provided by the caller
|
||||
for (MappingList::const_iterator iter = mapping_list_.begin();
|
||||
iter != mapping_list_.end();
|
||||
++iter) {
|
||||
DumpModule(iter->first, false, 0, iter->second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
|
||||
|
||||
const struct ucontext* const ucontext_;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
const google_breakpad::fpstate_t* const float_state_;
|
||||
#endif
|
||||
LinuxDumper* dumper_;
|
||||
const MappingList& mapping_list_;
|
||||
const MicrodumpExtraInfo microdump_extra_info_;
|
||||
char* log_line_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
if (blob) {
|
||||
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||
return false;
|
||||
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||
dumper.set_crash_address(
|
||||
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
|
||||
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,65 +0,0 @@
|
||||
// Copyright (c) 2014, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/mapping_info.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct MicrodumpExtraInfo;
|
||||
|
||||
// Writes a microdump (a reduced dump containing only the state of the crashing
|
||||
// thread) on the console (logcat on Android). These functions do not malloc nor
|
||||
// use libc functions which may. Thus, it can be used in contexts where the
|
||||
// state of the heap may be corrupt.
|
||||
// Args:
|
||||
// crashing_process: the pid of the crashing process. This must be trusted.
|
||||
// blob: a blob of data from the crashing process. See exception_handler.h
|
||||
// blob_size: the length of |blob| in bytes.
|
||||
// mappings: a list of additional mappings provided by the application.
|
||||
// build_fingerprint: a (optional) C string which determines the OS
|
||||
// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys).
|
||||
// product_info: a (optional) C string which determines the product name and
|
||||
// version (e.g., WebView:42.0.2311.136).
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
@ -1,257 +0,0 @@
|
||||
// Copyright (c) 2014 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test MicrodumpWriterTest;
|
||||
|
||||
MicrodumpExtraInfo MakeMicrodumpExtraInfo(
|
||||
const char* build_fingerprint,
|
||||
const char* product_info,
|
||||
const char* gpu_fingerprint) {
|
||||
MicrodumpExtraInfo info;
|
||||
info.build_fingerprint = build_fingerprint;
|
||||
info.product_info = product_info;
|
||||
info.gpu_fingerprint = gpu_fingerprint;
|
||||
return info;
|
||||
}
|
||||
|
||||
void CrashAndGetMicrodump(
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
scoped_array<char>* buf) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string stderr_file = temp_dir.path() + "/stderr.log";
|
||||
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
ASSERT_NE(-1, err_fd);
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
// Set a non-zero tid to avoid tripping asserts.
|
||||
context.tid = child;
|
||||
|
||||
// Redirect temporarily stderr to the stderr.log file.
|
||||
int save_err = dup(STDERR_FILENO);
|
||||
ASSERT_NE(-1, save_err);
|
||||
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
|
||||
|
||||
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
|
||||
microdump_extra_info));
|
||||
|
||||
// Revert stderr back to the console.
|
||||
dup2(save_err, STDERR_FILENO);
|
||||
close(save_err);
|
||||
|
||||
// Read back the stderr file and check for the microdump marker.
|
||||
fsync(err_fd);
|
||||
lseek(err_fd, 0, SEEK_SET);
|
||||
const size_t kBufSize = 64 * 1024;
|
||||
buf->reset(new char[kBufSize]);
|
||||
ASSERT_GT(read(err_fd, buf->get(), kBufSize), 0);
|
||||
|
||||
close(err_fd);
|
||||
close(fds[1]);
|
||||
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----BEGIN BREAKPAD MICRODUMP-----"));
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----END BREAKPAD MICRODUMP-----"));
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
const MicrodumpExtraInfo& expected_info) {
|
||||
std::istringstream iss(microdump_content);
|
||||
bool did_find_os_info = false;
|
||||
bool did_find_product_info = false;
|
||||
bool did_find_gpu_info = false;
|
||||
for (string line; std::getline(iss, line);) {
|
||||
if (line.find("O ") == 0) {
|
||||
std::istringstream os_info_tokens(line);
|
||||
string token;
|
||||
os_info_tokens.ignore(2); // Ignore the "O " preamble.
|
||||
// Check the OS descriptor char (L=Linux, A=Android).
|
||||
os_info_tokens >> token;
|
||||
ASSERT_TRUE(token == "L" || token == "A");
|
||||
|
||||
os_info_tokens >> token; // HW architecture.
|
||||
os_info_tokens >> token; // Number of cpus.
|
||||
for (size_t i = 0; i < token.size(); ++i)
|
||||
ASSERT_TRUE(isxdigit(token[i]));
|
||||
os_info_tokens >> token; // SW architecture.
|
||||
|
||||
// Check that the build fingerprint is in the right place.
|
||||
os_info_tokens >> token;
|
||||
if (expected_info.build_fingerprint)
|
||||
ASSERT_EQ(expected_info.build_fingerprint, token);
|
||||
did_find_os_info = true;
|
||||
} else if (line.find("V ") == 0) {
|
||||
if (expected_info.product_info)
|
||||
ASSERT_EQ(string("V ") + expected_info.product_info, line);
|
||||
did_find_product_info = true;
|
||||
} else if (line.find("G ") == 0) {
|
||||
if (expected_info.gpu_fingerprint)
|
||||
ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line);
|
||||
did_find_gpu_info = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(did_find_os_info);
|
||||
ASSERT_TRUE(did_find_product_info);
|
||||
ASSERT_TRUE(did_find_gpu_info);
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
const string& expected_fingerprint,
|
||||
const string& expected_product_info,
|
||||
const string& expected_gpu_fingerprint) {
|
||||
CheckMicrodumpContents(
|
||||
microdump_content,
|
||||
MakeMicrodumpExtraInfo(expected_fingerprint.c_str(),
|
||||
expected_product_info.c_str(),
|
||||
expected_gpu_fingerprint.c_str()));
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, BasicWithMappings) {
|
||||
// Push some extra mapping to check the MappingList logic.
|
||||
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
|
||||
const char* kMemoryName = "libfoo.so";
|
||||
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
|
||||
MappingInfo info;
|
||||
info.start_addr = memory_size;
|
||||
info.size = memory_size;
|
||||
info.offset = 42;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
MappingEntry mapping;
|
||||
mapping.first = info;
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
|
||||
scoped_array<char> buf;
|
||||
CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
|
||||
|
||||
#ifdef __LP64__
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#else
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 00001000 0000002A 00001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#endif
|
||||
|
||||
// In absence of a product info in the minidump, the writer should just write
|
||||
// an unknown marker.
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "V UNKNOWN:0.0.0.0"));
|
||||
}
|
||||
|
||||
// Ensure that the product info and build fingerprint metadata show up in the
|
||||
// final microdump if present.
|
||||
TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
const char kGPUFingerprint[] =
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoProductInfo) {
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
const char kGPUFingerprint[] = "bazqux";
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
"UNKNOWN:0.0.0.0", kGPUFingerprint);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoGPUInfo) {
|
||||
const char kProductInfo[] = "bazqux";
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
kProductInfo, "UNKNOWN");
|
||||
}
|
||||
} // namespace
|
@ -1,144 +0,0 @@
|
||||
// Copyright (c) 2013, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Helper class used to model a set of CPUs, as read from sysfs
|
||||
// files like /sys/devices/system/cpu/present
|
||||
// See See http://www.kernel.org/doc/Documentation/cputopology.txt
|
||||
class CpuSet {
|
||||
public:
|
||||
// The maximum number of supported CPUs.
|
||||
static const size_t kMaxCpus = 1024;
|
||||
|
||||
CpuSet() {
|
||||
my_memset(mask_, 0, sizeof(mask_));
|
||||
}
|
||||
|
||||
// Parse a sysfs file to extract the corresponding CPU set.
|
||||
bool ParseSysFile(int fd) {
|
||||
char buffer[512];
|
||||
int ret = sys_read(fd, buffer, sizeof(buffer)-1);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
buffer[ret] = '\0';
|
||||
|
||||
// Expected format: comma-separated list of items, where each
|
||||
// item can be a decimal integer, or two decimal integers separated
|
||||
// by a dash.
|
||||
// E.g.:
|
||||
// 0
|
||||
// 0,1,2,3
|
||||
// 0-3
|
||||
// 1,10-23
|
||||
const char* p = buffer;
|
||||
const char* p_end = p + ret;
|
||||
while (p < p_end) {
|
||||
// Skip leading space, if any
|
||||
while (p < p_end && my_isspace(*p))
|
||||
p++;
|
||||
|
||||
// Find start and size of current item.
|
||||
const char* item = p;
|
||||
size_t item_len = static_cast<size_t>(p_end - p);
|
||||
const char* item_next =
|
||||
static_cast<const char*>(my_memchr(p, ',', item_len));
|
||||
if (item_next != NULL) {
|
||||
p = item_next + 1;
|
||||
item_len = static_cast<size_t>(item_next - item);
|
||||
} else {
|
||||
p = p_end;
|
||||
item_next = p_end;
|
||||
}
|
||||
|
||||
// Ignore trailing spaces.
|
||||
while (item_next > item && my_isspace(item_next[-1]))
|
||||
item_next--;
|
||||
|
||||
// skip empty items.
|
||||
if (item_next == item)
|
||||
continue;
|
||||
|
||||
// read first decimal value.
|
||||
uintptr_t start = 0;
|
||||
const char* next = my_read_decimal_ptr(&start, item);
|
||||
uintptr_t end = start;
|
||||
if (*next == '-')
|
||||
my_read_decimal_ptr(&end, next+1);
|
||||
|
||||
while (start <= end)
|
||||
SetBit(start++);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Intersect this CPU set with another one.
|
||||
void IntersectWith(const CpuSet& other) {
|
||||
for (size_t nn = 0; nn < kMaskWordCount; ++nn)
|
||||
mask_[nn] &= other.mask_[nn];
|
||||
}
|
||||
|
||||
// Return the number of CPUs in this set.
|
||||
int GetCount() {
|
||||
int result = 0;
|
||||
for (size_t nn = 0; nn < kMaskWordCount; ++nn) {
|
||||
result += __builtin_popcount(mask_[nn]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetBit(uintptr_t index) {
|
||||
size_t nn = static_cast<size_t>(index);
|
||||
if (nn < kMaxCpus)
|
||||
mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits));
|
||||
}
|
||||
|
||||
typedef uint32_t MaskWordType;
|
||||
static const size_t kMaskWordBits = 8*sizeof(MaskWordType);
|
||||
static const size_t kMaskWordCount =
|
||||
(kMaxCpus + kMaskWordBits - 1) / kMaskWordBits;
|
||||
|
||||
MaskWordType mask_[kMaskWordCount];
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
|
@ -1,164 +0,0 @@
|
||||
// Copyright (c) 2013, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/cpu_set.h"
|
||||
#include "common/linux/tests/auto_testfile.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test CpuSetTest;
|
||||
|
||||
// Helper class to write test text file to a temporary file and return
|
||||
// its file descriptor.
|
||||
class ScopedTestFile : public AutoTestFile {
|
||||
public:
|
||||
explicit ScopedTestFile(const char* text)
|
||||
: AutoTestFile("cpu_set", text) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, EmptyCount) {
|
||||
CpuSet set;
|
||||
ASSERT_EQ(0, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, OneCpu) {
|
||||
ScopedTestFile file("10");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(1, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, OneCpuTerminated) {
|
||||
ScopedTestFile file("10\n");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(1, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, TwoCpusWithComma) {
|
||||
ScopedTestFile file("1,10");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(2, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, TwoCpusWithRange) {
|
||||
ScopedTestFile file("1-2");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(2, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, TenCpusWithRange) {
|
||||
ScopedTestFile file("9-18");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(10, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, MultiItems) {
|
||||
ScopedTestFile file("0, 2-4, 128");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
|
||||
CpuSet set;
|
||||
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
|
||||
ASSERT_EQ(5, set.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, IntersectWith) {
|
||||
ScopedTestFile file1("9-19");
|
||||
ASSERT_TRUE(file1.IsOk());
|
||||
CpuSet set1;
|
||||
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
|
||||
ASSERT_EQ(11, set1.GetCount());
|
||||
|
||||
ScopedTestFile file2("16-24");
|
||||
ASSERT_TRUE(file2.IsOk());
|
||||
CpuSet set2;
|
||||
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
|
||||
ASSERT_EQ(9, set2.GetCount());
|
||||
|
||||
set1.IntersectWith(set2);
|
||||
ASSERT_EQ(4, set1.GetCount());
|
||||
ASSERT_EQ(9, set2.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, SelfIntersection) {
|
||||
ScopedTestFile file1("9-19");
|
||||
ASSERT_TRUE(file1.IsOk());
|
||||
CpuSet set1;
|
||||
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
|
||||
ASSERT_EQ(11, set1.GetCount());
|
||||
|
||||
set1.IntersectWith(set1);
|
||||
ASSERT_EQ(11, set1.GetCount());
|
||||
}
|
||||
|
||||
TEST(CpuSetTest, EmptyIntersection) {
|
||||
ScopedTestFile file1("0-19");
|
||||
ASSERT_TRUE(file1.IsOk());
|
||||
CpuSet set1;
|
||||
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
|
||||
ASSERT_EQ(20, set1.GetCount());
|
||||
|
||||
ScopedTestFile file2("20-39");
|
||||
ASSERT_TRUE(file2.IsOk());
|
||||
CpuSet set2;
|
||||
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
|
||||
ASSERT_EQ(20, set2.GetCount());
|
||||
|
||||
set1.IntersectWith(set2);
|
||||
ASSERT_EQ(0, set1.GetCount());
|
||||
|
||||
ASSERT_EQ(20, set2.GetCount());
|
||||
}
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -91,7 +90,7 @@ class DirectoryReader {
|
||||
reinterpret_cast<kernel_dirent*>(buf_);
|
||||
|
||||
buf_used_ -= dent->d_reclen;
|
||||
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
||||
memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "client/linux/minidump_writer/directory_reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
@ -45,7 +44,7 @@ typedef testing::Test DirectoryReaderTest;
|
||||
}
|
||||
|
||||
TEST(DirectoryReaderTest, CompareResults) {
|
||||
std::set<string> dent_set;
|
||||
std::set<std::string> dent_set;
|
||||
|
||||
DIR *const dir = opendir("/proc/self");
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -115,7 +114,7 @@ class LineReader {
|
||||
|
||||
assert(buf_used_ >= len + 1);
|
||||
buf_used_ -= len + 1;
|
||||
my_memmove(buf_, buf_ + len + 1, buf_used_);
|
||||
memmove(buf_, buf_ + len + 1, buf_used_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -33,137 +33,158 @@
|
||||
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/tests/auto_testfile.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#define TEMPDIR "/tmp"
|
||||
#else
|
||||
#define TEMPDIR "/data/local/tmp"
|
||||
#endif
|
||||
|
||||
static int TemporaryFile() {
|
||||
static const char templ[] = TEMPDIR "/line-reader-unittest-XXXXXX";
|
||||
char templ_copy[sizeof(templ)];
|
||||
memcpy(templ_copy, templ, sizeof(templ));
|
||||
const int fd = mkstemp(templ_copy);
|
||||
if (fd >= 0)
|
||||
unlink(templ_copy);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test LineReaderTest;
|
||||
|
||||
class ScopedTestFile : public AutoTestFile {
|
||||
public:
|
||||
explicit ScopedTestFile(const char* text)
|
||||
: AutoTestFile("line_reader", text) {
|
||||
}
|
||||
|
||||
ScopedTestFile(const char* text, size_t text_len)
|
||||
: AutoTestFile("line_reader", text, text_len) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, EmptyFile) {
|
||||
ScopedTestFile file("");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
const int fd = TemporaryFile();
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, OneLineTerminated) {
|
||||
ScopedTestFile file("a\n");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
const int fd = TemporaryFile();
|
||||
write(fd, "a\n", 2);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned int len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned int)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
ASSERT_EQ(len, (unsigned int)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, OneLine) {
|
||||
ScopedTestFile file("a");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
const int fd = TemporaryFile();
|
||||
write(fd, "a", 1);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, TwoLinesTerminated) {
|
||||
ScopedTestFile file("a\nb\n");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
const int fd = TemporaryFile();
|
||||
write(fd, "a\nb\n", 4);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('b', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'b');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, TwoLines) {
|
||||
ScopedTestFile file("a\nb");
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
const int fd = TemporaryFile();
|
||||
write(fd, "a\nb", 3);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('a', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ((unsigned)1, len);
|
||||
ASSERT_EQ('b', line[0]);
|
||||
ASSERT_EQ('\0', line[1]);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'b');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, MaxLength) {
|
||||
char l[LineReader::kMaxLineLen-1];
|
||||
const int fd = TemporaryFile();
|
||||
char l[LineReader::kMaxLineLen - 1];
|
||||
memset(l, 'a', sizeof(l));
|
||||
ScopedTestFile file(l, sizeof(l));
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
write(fd, l, sizeof(l));
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(sizeof(l), len);
|
||||
ASSERT_EQ(len, sizeof(l));
|
||||
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
|
||||
ASSERT_EQ('\0', line[len]);
|
||||
ASSERT_EQ(line[len], 0);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST(LineReaderTest, TooLong) {
|
||||
// Note: this writes kMaxLineLen 'a' chars in the test file.
|
||||
const int fd = TemporaryFile();
|
||||
char l[LineReader::kMaxLineLen];
|
||||
memset(l, 'a', sizeof(l));
|
||||
ScopedTestFile file(l, sizeof(l));
|
||||
ASSERT_TRUE(file.IsOk());
|
||||
LineReader reader(file.GetFd());
|
||||
write(fd, l, sizeof(l));
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
@ -1,258 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
|
||||
// See linux_core_dumper.h for details.
|
||||
|
||||
#include "client/linux/minidump_writer/linux_core_dumper.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/procfs.h>
|
||||
#if defined(__mips__) && defined(__ANDROID__)
|
||||
// To get register definitions.
|
||||
#include <asm/reg.h>
|
||||
#endif
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
|
||||
const char* core_path,
|
||||
const char* procfs_path,
|
||||
const char* root_prefix)
|
||||
: LinuxDumper(pid, root_prefix),
|
||||
core_path_(core_path),
|
||||
procfs_path_(procfs_path),
|
||||
thread_infos_(&allocator_, 8) {
|
||||
assert(core_path_);
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
|
||||
const char* node) const {
|
||||
if (!path || !node)
|
||||
return false;
|
||||
|
||||
size_t node_len = my_strlen(node);
|
||||
if (node_len == 0)
|
||||
return false;
|
||||
|
||||
size_t procfs_path_len = my_strlen(procfs_path_);
|
||||
size_t total_length = procfs_path_len + 1 + node_len;
|
||||
if (total_length >= NAME_MAX)
|
||||
return false;
|
||||
|
||||
memcpy(path, procfs_path_, procfs_path_len);
|
||||
path[procfs_path_len] = '/';
|
||||
memcpy(path + procfs_path_len + 1, node, node_len);
|
||||
path[total_length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
const void* src, size_t length) {
|
||||
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
|
||||
// TODO(benchan): Investigate whether the data to be copied could span
|
||||
// across multiple segments in the core dump file. ElfCoreDump::CopyData
|
||||
// and this method do not handle that case yet.
|
||||
if (!core_.CopyData(dest, virtual_address, length)) {
|
||||
// If the data segment is not found in the core dump, fill the result
|
||||
// with marker characters.
|
||||
memset(dest, 0xab, length);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
if (index >= thread_infos_.size())
|
||||
return false;
|
||||
|
||||
*info = thread_infos_[index];
|
||||
const uint8_t* stack_pointer;
|
||||
#if defined(__i386)
|
||||
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||
#elif defined(__aarch64__)
|
||||
memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||
#elif defined(__mips__)
|
||||
stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::IsPostMortem() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::ThreadsSuspend() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::ThreadsResume() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::EnumerateThreads() {
|
||||
if (!mapped_core_file_.Map(core_path_, 0)) {
|
||||
fprintf(stderr, "Could not map core dump file into memory\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
core_.SetContent(mapped_core_file_.content());
|
||||
if (!core_.IsValid()) {
|
||||
fprintf(stderr, "Invalid core dump file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfCoreDump::Note note = core_.GetFirstNote();
|
||||
if (!note.IsValid()) {
|
||||
fprintf(stderr, "PT_NOTE section not found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool first_thread = true;
|
||||
do {
|
||||
ElfCoreDump::Word type = note.GetType();
|
||||
MemoryRange name = note.GetName();
|
||||
MemoryRange description = note.GetDescription();
|
||||
|
||||
if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
|
||||
fprintf(stderr, "Could not found a valid PT_NOTE.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
|
||||
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
|
||||
// Thread Name Type
|
||||
// -------------------------------------------------------------------
|
||||
// 1st thread CORE NT_PRSTATUS
|
||||
// process-wide CORE NT_PRPSINFO
|
||||
// process-wide CORE NT_AUXV
|
||||
// 1st thread CORE NT_FPREGSET
|
||||
// 1st thread LINUX NT_PRXFPREG
|
||||
// 1st thread LINUX NT_386_TLS
|
||||
//
|
||||
// 2nd thread CORE NT_PRSTATUS
|
||||
// 2nd thread CORE NT_FPREGSET
|
||||
// 2nd thread LINUX NT_PRXFPREG
|
||||
// 2nd thread LINUX NT_386_TLS
|
||||
//
|
||||
// 3rd thread CORE NT_PRSTATUS
|
||||
// 3rd thread CORE NT_FPREGSET
|
||||
// 3rd thread LINUX NT_PRXFPREG
|
||||
// 3rd thread LINUX NT_386_TLS
|
||||
//
|
||||
// The following code only works if notes are ordered as expected.
|
||||
switch (type) {
|
||||
case NT_PRSTATUS: {
|
||||
if (description.length() != sizeof(elf_prstatus)) {
|
||||
fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const elf_prstatus* status =
|
||||
reinterpret_cast<const elf_prstatus*>(description.data());
|
||||
pid_t pid = status->pr_pid;
|
||||
ThreadInfo info;
|
||||
memset(&info, 0, sizeof(ThreadInfo));
|
||||
info.tgid = status->pr_pgrp;
|
||||
info.ppid = status->pr_ppid;
|
||||
#if defined(__mips__)
|
||||
#if defined(__ANDROID__)
|
||||
for (int i = EF_R0; i <= EF_R31; i++)
|
||||
info.mcontext.gregs[i - EF_R0] = status->pr_reg[i];
|
||||
#else // __ANDROID__
|
||||
for (int i = EF_REG0; i <= EF_REG31; i++)
|
||||
info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i];
|
||||
#endif // __ANDROID__
|
||||
info.mcontext.mdlo = status->pr_reg[EF_LO];
|
||||
info.mcontext.mdhi = status->pr_reg[EF_HI];
|
||||
info.mcontext.pc = status->pr_reg[EF_CP0_EPC];
|
||||
#else // __mips__
|
||||
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
|
||||
#endif // __mips__
|
||||
if (first_thread) {
|
||||
crash_thread_ = pid;
|
||||
crash_signal_ = status->pr_info.si_signo;
|
||||
}
|
||||
first_thread = false;
|
||||
threads_.push_back(pid);
|
||||
thread_infos_.push_back(info);
|
||||
break;
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
case NT_FPREGSET: {
|
||||
if (thread_infos_.empty())
|
||||
return false;
|
||||
|
||||
ThreadInfo* info = &thread_infos_.back();
|
||||
if (description.length() != sizeof(info->fpregs)) {
|
||||
fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if defined(__i386)
|
||||
case NT_PRXFPREG: {
|
||||
if (thread_infos_.empty())
|
||||
return false;
|
||||
|
||||
ThreadInfo* info = &thread_infos_.back();
|
||||
if (description.length() != sizeof(info->fpxregs)) {
|
||||
fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
note = note.GetNextNote();
|
||||
} while (note.IsValid());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -1,125 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
|
||||
// class, which is derived from google_breakpad::LinuxDumper to extract
|
||||
// information from a crashed process via its core dump and proc files.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "common/linux/elf_core_dump.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class LinuxCoreDumper : public LinuxDumper {
|
||||
public:
|
||||
// Constructs a dumper for extracting information of a given process
|
||||
// with a process ID of |pid| via its core dump file at |core_path| and
|
||||
// its proc files at |procfs_path|. If |procfs_path| is a copy of
|
||||
// /proc/<pid>, it should contain the following files:
|
||||
// auxv, cmdline, environ, exe, maps, status
|
||||
// See LinuxDumper for the purpose of |root_prefix|.
|
||||
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path,
|
||||
const char* root_prefix = "");
|
||||
|
||||
// Implements LinuxDumper::BuildProcPath().
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||
// result.|node| is the final node without any slashes. Return true on
|
||||
// success.
|
||||
//
|
||||
// As this dumper performs a post-mortem dump and makes use of a copy
|
||||
// of the proc files of the crashed process, this derived method does
|
||||
// not actually make use of |pid| and always returns a subpath of
|
||||
// |procfs_path_| regardless of whether |pid| corresponds to the main
|
||||
// process or a thread of the process, i.e. assuming both the main process
|
||||
// and its threads have the following proc files with the same content:
|
||||
// auxv, cmdline, environ, exe, maps, status
|
||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
|
||||
|
||||
// Implements LinuxDumper::CopyFromProcess().
|
||||
// Copies content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. This method extracts the content
|
||||
// the core dump and fills |dest| with a sequence of marker bytes
|
||||
// if the expected data is not found in the core dump. Returns true if
|
||||
// the expected data is found in the core dump.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
|
||||
// Implements LinuxDumper::GetThreadInfoByIndex().
|
||||
// Reads information about the |index|-th thread of |threads_|.
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
|
||||
|
||||
// Implements LinuxDumper::IsPostMortem().
|
||||
// Always returns true to indicate that this dumper performs a
|
||||
// post-mortem dump of a crashed process via a core dump file.
|
||||
virtual bool IsPostMortem() const;
|
||||
|
||||
// Implements LinuxDumper::ThreadsSuspend().
|
||||
// As the dumper performs a post-mortem dump via a core dump file,
|
||||
// there is no threads to suspend. This method does nothing and
|
||||
// always returns true.
|
||||
virtual bool ThreadsSuspend();
|
||||
|
||||
// Implements LinuxDumper::ThreadsResume().
|
||||
// As the dumper performs a post-mortem dump via a core dump file,
|
||||
// there is no threads to resume. This method does nothing and
|
||||
// always returns true.
|
||||
virtual bool ThreadsResume();
|
||||
|
||||
protected:
|
||||
// Implements LinuxDumper::EnumerateThreads().
|
||||
// Enumerates all threads of the given process into |threads_|.
|
||||
virtual bool EnumerateThreads();
|
||||
|
||||
private:
|
||||
// Path of the core dump file.
|
||||
const char* core_path_;
|
||||
|
||||
// Path of the directory containing the proc files of the given process,
|
||||
// which is usually a copy of /proc/<pid>.
|
||||
const char* procfs_path_;
|
||||
|
||||
// Memory-mapped core dump file at |core_path_|.
|
||||
MemoryMappedFile mapped_core_file_;
|
||||
|
||||
// Content of the core dump file.
|
||||
ElfCoreDump core_;
|
||||
|
||||
// Thread info found in the core dump file.
|
||||
wasteful_vector<ThreadInfo> thread_infos_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_
|
@ -1,128 +0,0 @@
|
||||
// Copyright (c) 2012, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// linux_core_dumper_unittest.cc:
|
||||
// Unit tests for google_breakpad::LinuxCoreDumoer.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/linux_core_dumper.h"
|
||||
#include "common/linux/tests/crash_generator.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
|
||||
const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
|
||||
const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" };
|
||||
|
||||
char path[PATH_MAX];
|
||||
dumper.GetMappingAbsolutePath(mapping, path);
|
||||
|
||||
EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path);
|
||||
}
|
||||
|
||||
TEST(LinuxCoreDumperTest, BuildProcPath) {
|
||||
const pid_t pid = getpid();
|
||||
const char procfs_path[] = "/procfs_copy";
|
||||
LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
|
||||
|
||||
char maps_path[NAME_MAX] = "";
|
||||
char maps_path_expected[NAME_MAX];
|
||||
snprintf(maps_path_expected, sizeof(maps_path_expected),
|
||||
"%s/maps", procfs_path);
|
||||
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
|
||||
EXPECT_STREQ(maps_path_expected, maps_path);
|
||||
|
||||
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
|
||||
|
||||
char long_node[NAME_MAX];
|
||||
size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
|
||||
memset(long_node, 'a', long_node_len);
|
||||
long_node[long_node_len] = '\0';
|
||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
|
||||
}
|
||||
|
||||
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
|
||||
CrashGenerator crash_generator;
|
||||
if (!crash_generator.HasDefaultCorePattern()) {
|
||||
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
|
||||
"is skipped due to non-default core pattern\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned kNumOfThreads = 3;
|
||||
const unsigned kCrashThread = 1;
|
||||
const int kCrashSignal = SIGABRT;
|
||||
pid_t child_pid;
|
||||
ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
|
||||
kCrashSignal, &child_pid));
|
||||
|
||||
const string core_file = crash_generator.GetCoreFilePath();
|
||||
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
struct stat st;
|
||||
if (stat(core_file.c_str(), &st) != 0) {
|
||||
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
|
||||
"skipped due to no core file being generated");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
|
||||
|
||||
EXPECT_TRUE(dumper.Init());
|
||||
|
||||
EXPECT_TRUE(dumper.IsPostMortem());
|
||||
|
||||
// These are no-ops and should always return true.
|
||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
|
||||
// LinuxCoreDumper cannot determine the crash address and thus it always
|
||||
// sets the crash address to 0.
|
||||
EXPECT_EQ(0U, dumper.crash_address());
|
||||
EXPECT_EQ(kCrashSignal, dumper.crash_signal());
|
||||
EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
|
||||
dumper.crash_thread());
|
||||
|
||||
EXPECT_EQ(kNumOfThreads, dumper.threads().size());
|
||||
for (unsigned i = 0; i < kNumOfThreads; ++i) {
|
||||
ThreadInfo info;
|
||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
|
||||
EXPECT_EQ(getpid(), info.ppid);
|
||||
}
|
||||
}
|
@ -27,9 +27,6 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
|
||||
// See linux_dumper.h for details.
|
||||
|
||||
// This code deals with the mechanics of getting information about a crashed
|
||||
// process. Since this code may run in a compromised address space, the same
|
||||
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
|
||||
@ -37,40 +34,75 @@
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <link.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "client/linux/minidump_writer/directory_reader.h"
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "common/linux/elfutils.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
// Android packed relocations definitions are not yet available from the
|
||||
// NDK header files, so we have to provide them manually here.
|
||||
#ifndef DT_LOOS
|
||||
#define DT_LOOS 0x6000000d
|
||||
#endif
|
||||
#ifndef DT_ANDROID_REL
|
||||
static const int DT_ANDROID_REL = DT_LOOS + 2;
|
||||
#endif
|
||||
#ifndef DT_ANDROID_RELA
|
||||
static const int DT_ANDROID_RELA = DT_LOOS + 4;
|
||||
#endif
|
||||
|
||||
#endif // __ANDROID __
|
||||
|
||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
||||
static const char kDeletedSuffix[] = " (deleted)";
|
||||
|
||||
// Suspend a thread by attaching to it.
|
||||
static bool SuspendThread(pid_t pid) {
|
||||
// This may fail if the thread has just died or debugged.
|
||||
errno = 0;
|
||||
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
|
||||
errno != 0) {
|
||||
return false;
|
||||
}
|
||||
while (sys_waitpid(pid, NULL, __WALL) < 0) {
|
||||
if (errno != EINTR) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
||||
// the seccomp sandbox. Not only does this cause difficulties down the line
|
||||
// when trying to dump the thread's stack, it also results in the minidumps
|
||||
// containing information about the trusted threads. This information is
|
||||
// generally completely meaningless and just pollutes the minidumps.
|
||||
// We thus test the stack pointer and exclude any threads that are part of
|
||||
// the seccomp sandbox's trusted code.
|
||||
user_regs_struct regs;
|
||||
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 ||
|
||||
#if defined(__i386)
|
||||
!regs.esp
|
||||
#elif defined(__x86_64)
|
||||
!regs.rsp
|
||||
#endif
|
||||
) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resume a thread by detaching from it.
|
||||
static bool ResumeThread(pid_t pid) {
|
||||
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
|
||||
}
|
||||
|
||||
inline static bool IsMappedFileOpenUnsafe(
|
||||
const google_breakpad::MappingInfo& mapping) {
|
||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
||||
@ -84,238 +116,195 @@ inline static bool IsMappedFileOpenUnsafe(
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
||||
#define AT_MAX AT_SYSINFO_EHDR
|
||||
|
||||
LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
|
||||
LinuxDumper::LinuxDumper(int pid)
|
||||
: pid_(pid),
|
||||
root_prefix_(root_prefix),
|
||||
crash_address_(0),
|
||||
crash_signal_(0),
|
||||
crash_thread_(pid),
|
||||
threads_suspended_(false),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_),
|
||||
auxv_(&allocator_, AT_MAX + 1) {
|
||||
assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX);
|
||||
// The passed-in size to the constructor (above) is only a hint.
|
||||
// Must call .resize() to do actual initialization of the elements.
|
||||
auxv_.resize(AT_MAX + 1);
|
||||
}
|
||||
|
||||
LinuxDumper::~LinuxDumper() {
|
||||
mappings_(&allocator_) {
|
||||
}
|
||||
|
||||
bool LinuxDumper::Init() {
|
||||
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
|
||||
return EnumerateThreads(&threads_) &&
|
||||
EnumerateMappings(&mappings_);
|
||||
}
|
||||
|
||||
bool LinuxDumper::LateInit() {
|
||||
#if defined(__ANDROID__)
|
||||
LatePostprocessMappings();
|
||||
#endif
|
||||
return true;
|
||||
bool LinuxDumper::ThreadsSuspend() {
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
if (!SuspendThread(threads_[i])) {
|
||||
// If the thread either disappeared before we could attach to it, or if
|
||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||
// silently drop it from the minidump.
|
||||
memmove(&threads_[i], &threads_[i+1],
|
||||
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||
threads_.resize(threads_.size() - 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
threads_suspended_ = true;
|
||||
return threads_.size() > 0;
|
||||
}
|
||||
|
||||
bool LinuxDumper::ThreadsResume() {
|
||||
if (!threads_suspended_)
|
||||
return false;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= ResumeThread(threads_[i]);
|
||||
threads_suspended_ = false;
|
||||
return good;
|
||||
}
|
||||
|
||||
void
|
||||
LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const {
|
||||
assert(path);
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
path[0] = '\0';
|
||||
|
||||
const unsigned pid_len = my_int_len(pid);
|
||||
|
||||
assert(node);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t node_len = my_strlen(node);
|
||||
assert(node_len < NAME_MAX);
|
||||
if (node_len >= NAME_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(node_len > 0);
|
||||
if (node_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pid > 0);
|
||||
if (pid <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t total_length = 6 + pid_len + 1 + node_len;
|
||||
|
||||
assert(total_length < NAME_MAX);
|
||||
if (total_length >= NAME_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(path, "/proc/", 6);
|
||||
my_itos(path + 6, pid, pid_len);
|
||||
memcpy(path + 6 + pid_len, "/", 1);
|
||||
memcpy(path + 6 + pid_len + 1, node, node_len);
|
||||
path[total_length] = '\0';
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
uint8_t identifier[sizeof(MDGUID)])
|
||||
{
|
||||
assert(!member || mapping_id < mappings_.size());
|
||||
my_memset(identifier, 0, sizeof(MDGUID));
|
||||
if (IsMappedFileOpenUnsafe(mapping))
|
||||
return false;
|
||||
|
||||
// Special-case linux-gate because it's not a real file.
|
||||
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
|
||||
const uintptr_t kPageSize = getpagesize();
|
||||
void* linux_gate = NULL;
|
||||
if (pid_ == sys_getpid()) {
|
||||
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
|
||||
} else {
|
||||
linux_gate = allocator_.Alloc(mapping.size);
|
||||
linux_gate = allocator_.Alloc(kPageSize);
|
||||
CopyFromProcess(linux_gate, pid_,
|
||||
reinterpret_cast<const void*>(mapping.start_addr),
|
||||
mapping.size);
|
||||
kPageSize);
|
||||
}
|
||||
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
|
||||
}
|
||||
|
||||
char filename[PATH_MAX];
|
||||
if (!GetMappingAbsolutePath(mapping, filename))
|
||||
char filename[NAME_MAX];
|
||||
size_t filename_len = my_strlen(mapping.name);
|
||||
assert(filename_len < NAME_MAX);
|
||||
if (filename_len >= NAME_MAX)
|
||||
return false;
|
||||
memcpy(filename, mapping.name, filename_len);
|
||||
filename[filename_len] = '\0';
|
||||
bool filename_modified = HandleDeletedFileInMapping(filename);
|
||||
|
||||
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||
if (!mapped_file.data() || mapped_file.size() < SELFMAG)
|
||||
int fd = sys_open(filename, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct kernel_stat st;
|
||||
if (sys_fstat(fd, &st) != 0) {
|
||||
sys_close(fd);
|
||||
return false;
|
||||
}
|
||||
#if defined(__x86_64)
|
||||
#define sys_mmap2 sys_mmap
|
||||
#endif
|
||||
void* base = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
sys_close(fd);
|
||||
if (base == MAP_FAILED)
|
||||
return false;
|
||||
|
||||
bool success =
|
||||
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||
bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier);
|
||||
sys_munmap(base, st.st_size);
|
||||
if (success && member && filename_modified) {
|
||||
mappings_[mapping_id]->name[my_strlen(mapping.name) -
|
||||
mappings_[mapping_id]->name[filename_len -
|
||||
sizeof(kDeletedSuffix) + 1] = '\0';
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
|
||||
char path[PATH_MAX]) const {
|
||||
return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX &&
|
||||
my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ElfFileSoNameFromMappedFile(
|
||||
const void* elf_base, char* soname, size_t soname_size) {
|
||||
if (!IsValidElf(elf_base)) {
|
||||
// Not ELF
|
||||
return false;
|
||||
}
|
||||
|
||||
const void* segment_start;
|
||||
size_t segment_size;
|
||||
int elf_class;
|
||||
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
|
||||
&segment_start, &segment_size, &elf_class)) {
|
||||
// No dynamic section
|
||||
return false;
|
||||
}
|
||||
|
||||
const void* dynstr_start;
|
||||
size_t dynstr_size;
|
||||
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
|
||||
&dynstr_start, &dynstr_size, &elf_class)) {
|
||||
// No dynstr section
|
||||
return false;
|
||||
}
|
||||
|
||||
const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start);
|
||||
size_t dcount = segment_size / sizeof(ElfW(Dyn));
|
||||
for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) {
|
||||
if (dyn->d_tag == DT_SONAME) {
|
||||
const char* dynstr = static_cast<const char*>(dynstr_start);
|
||||
if (dyn->d_un.d_val >= dynstr_size) {
|
||||
// Beyond the end of the dynstr section
|
||||
return false;
|
||||
}
|
||||
const char* str = dynstr + dyn->d_un.d_val;
|
||||
const size_t maxsize = dynstr_size - dyn->d_un.d_val;
|
||||
my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Did not find SONAME
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the shared object name (SONAME) by examining the ELF information
|
||||
// for |mapping|. If the SONAME is found copy it into the passed buffer
|
||||
// |soname| and return true. The size of the buffer is |soname_size|.
|
||||
// The SONAME will be truncated if it is too long to fit in the buffer.
|
||||
bool ElfFileSoName(const LinuxDumper& dumper,
|
||||
const MappingInfo& mapping, char* soname, size_t soname_size) {
|
||||
if (IsMappedFileOpenUnsafe(mapping)) {
|
||||
// Not safe
|
||||
return false;
|
||||
}
|
||||
|
||||
char filename[PATH_MAX];
|
||||
if (!dumper.GetMappingAbsolutePath(mapping, filename))
|
||||
return false;
|
||||
|
||||
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||
if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
|
||||
// mmap failed
|
||||
return false;
|
||||
}
|
||||
|
||||
return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size) {
|
||||
my_strlcpy(file_path, mapping.name, file_path_size);
|
||||
|
||||
// If an executable is mapped from a non-zero offset, this is likely because
|
||||
// the executable was loaded directly from inside an archive file (e.g., an
|
||||
// apk on Android). We try to find the name of the shared object (SONAME) by
|
||||
// looking in the file for ELF sections.
|
||||
bool mapped_from_archive = false;
|
||||
if (mapping.exec && mapping.offset != 0) {
|
||||
mapped_from_archive =
|
||||
ElfFileSoName(*this, mapping, file_name, file_name_size);
|
||||
}
|
||||
|
||||
if (mapped_from_archive) {
|
||||
// Some tools (e.g., stackwalk) extract the basename from the pathname. In
|
||||
// this case, we append the file_name to the mapped archive path as follows:
|
||||
// file_name := libname.so
|
||||
// file_path := /path/to/ARCHIVE.APK/libname.so
|
||||
if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) {
|
||||
my_strlcat(file_path, "/", file_path_size);
|
||||
my_strlcat(file_path, file_name, file_path_size);
|
||||
}
|
||||
} else {
|
||||
// Common case:
|
||||
// file_path := /path/to/libname.so
|
||||
// file_name := libname.so
|
||||
const char* basename = my_strrchr(file_path, '/');
|
||||
basename = basename == NULL ? file_path : (basename + 1);
|
||||
my_strlcpy(file_name, basename, file_name_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxDumper::ReadAuxv() {
|
||||
void*
|
||||
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
|
||||
char auxv_path[NAME_MAX];
|
||||
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
|
||||
return false;
|
||||
}
|
||||
BuildProcPath(auxv_path, pid, "auxv");
|
||||
|
||||
// If BuildProcPath errors out due to invalid input, we'll handle it when
|
||||
// we try to sys_open the file.
|
||||
|
||||
// Find the AT_SYSINFO_EHDR entry for linux-gate.so
|
||||
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
||||
// information.
|
||||
int fd = sys_open(auxv_path, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
elf_aux_entry one_aux_entry;
|
||||
bool res = false;
|
||||
while (sys_read(fd,
|
||||
&one_aux_entry,
|
||||
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
|
||||
one_aux_entry.a_type != AT_NULL) {
|
||||
if (one_aux_entry.a_type <= AT_MAX) {
|
||||
auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
|
||||
res = true;
|
||||
if (one_aux_entry.a_type == AT_SYSINFO_EHDR) {
|
||||
close(fd);
|
||||
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
|
||||
}
|
||||
}
|
||||
sys_close(fd);
|
||||
return res;
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool LinuxDumper::EnumerateMappings() {
|
||||
bool
|
||||
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
|
||||
char maps_path[NAME_MAX];
|
||||
if (!BuildProcPath(maps_path, pid_, "maps"))
|
||||
return false;
|
||||
BuildProcPath(maps_path, pid_, "maps");
|
||||
|
||||
// linux_gate_loc is the beginning of the kernel's mapping of
|
||||
// linux-gate.so in the process. It doesn't actually show up in the
|
||||
// maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
|
||||
// aux vector entry, which gives the information necessary to special
|
||||
// case its entry when creating the list of mappings.
|
||||
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
||||
// information.
|
||||
const void* linux_gate_loc =
|
||||
reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
|
||||
// Although the initial executable is usually the first mapping, it's not
|
||||
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
|
||||
// actual entry point to find the mapping.
|
||||
const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
|
||||
// maps list as a filename, so we use the aux vector to find it's
|
||||
// load location and special case it's entry when creating the list
|
||||
// of mappings.
|
||||
const void* linux_gate_loc;
|
||||
linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_);
|
||||
|
||||
const int fd = sys_open(maps_path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
@ -331,7 +320,6 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
if (*i1 == '-') {
|
||||
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
|
||||
if (*i2 == ' ') {
|
||||
bool exec = (*(i2 + 3) == 'x');
|
||||
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
|
||||
if (*i3 == ' ') {
|
||||
const char* name = NULL;
|
||||
@ -345,47 +333,27 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
}
|
||||
// Merge adjacent mappings with the same name into one module,
|
||||
// assuming they're a single library mapped by the dynamic linker
|
||||
if (name && !mappings_.empty()) {
|
||||
MappingInfo* module = mappings_.back();
|
||||
if (name && result->size()) {
|
||||
MappingInfo* module = (*result)[result->size() - 1];
|
||||
if ((start_addr == module->start_addr + module->size) &&
|
||||
(my_strlen(name) == my_strlen(module->name)) &&
|
||||
(my_strncmp(name, module->name, my_strlen(name)) == 0) &&
|
||||
(exec == module->exec)) {
|
||||
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
|
||||
module->size = end_addr - module->start_addr;
|
||||
line_reader->PopLine(line_len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||
my_memset(module, 0, sizeof(MappingInfo));
|
||||
memset(module, 0, sizeof(MappingInfo));
|
||||
module->start_addr = start_addr;
|
||||
module->size = end_addr - start_addr;
|
||||
module->offset = offset;
|
||||
module->exec = exec;
|
||||
if (name != NULL) {
|
||||
const unsigned l = my_strlen(name);
|
||||
if (l < sizeof(module->name))
|
||||
my_memcpy(module->name, name, l);
|
||||
}
|
||||
// If this is the entry-point mapping, and it's not already the
|
||||
// first one, then we need to make it be first. This is because
|
||||
// the minidump format assumes the first module is the one that
|
||||
// corresponds to the main executable (as codified in
|
||||
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
|
||||
if (entry_point_loc &&
|
||||
(entry_point_loc >=
|
||||
reinterpret_cast<void*>(module->start_addr)) &&
|
||||
(entry_point_loc <
|
||||
reinterpret_cast<void*>(module->start_addr+module->size)) &&
|
||||
!mappings_.empty()) {
|
||||
// push the module onto the front of the list.
|
||||
mappings_.resize(mappings_.size() + 1);
|
||||
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
|
||||
mappings_[idx] = mappings_[idx - 1];
|
||||
mappings_[0] = module;
|
||||
} else {
|
||||
mappings_.push_back(module);
|
||||
memcpy(module->name, name, l);
|
||||
}
|
||||
result->push_back(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -394,115 +362,115 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
|
||||
sys_close(fd);
|
||||
|
||||
return !mappings_.empty();
|
||||
return result->size() > 0;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Parse /proc/$pid/task to list all the threads of the process identified by
|
||||
// pid.
|
||||
bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
|
||||
char task_path[NAME_MAX];
|
||||
BuildProcPath(task_path, pid_, "task");
|
||||
|
||||
bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) {
|
||||
CopyFromProcess(ehdr, pid_,
|
||||
reinterpret_cast<const void*>(start_addr),
|
||||
sizeof(*ehdr));
|
||||
return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0;
|
||||
}
|
||||
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
|
||||
|
||||
void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
|
||||
uintptr_t start_addr,
|
||||
uintptr_t* min_vaddr_ptr,
|
||||
uintptr_t* dyn_vaddr_ptr,
|
||||
size_t* dyn_count_ptr) {
|
||||
uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
|
||||
|
||||
const uintptr_t max_addr = UINTPTR_MAX;
|
||||
uintptr_t min_vaddr = max_addr;
|
||||
uintptr_t dyn_vaddr = 0;
|
||||
size_t dyn_count = 0;
|
||||
|
||||
for (size_t i = 0; i < ehdr->e_phnum; ++i) {
|
||||
ElfW(Phdr) phdr;
|
||||
CopyFromProcess(&phdr, pid_,
|
||||
reinterpret_cast<const void*>(phdr_addr),
|
||||
sizeof(phdr));
|
||||
if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) {
|
||||
min_vaddr = phdr.p_vaddr;
|
||||
// The directory may contain duplicate entries which we filter by assuming
|
||||
// that they are consecutive.
|
||||
int last_tid = -1;
|
||||
const char* dent_name;
|
||||
while (dir_reader->GetNextEntry(&dent_name)) {
|
||||
if (my_strcmp(dent_name, ".") &&
|
||||
my_strcmp(dent_name, "..")) {
|
||||
int tid = 0;
|
||||
if (my_strtoui(&tid, dent_name) &&
|
||||
last_tid != tid) {
|
||||
last_tid = tid;
|
||||
result->push_back(tid);
|
||||
}
|
||||
}
|
||||
if (phdr.p_type == PT_DYNAMIC) {
|
||||
dyn_vaddr = phdr.p_vaddr;
|
||||
dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn));
|
||||
}
|
||||
phdr_addr += sizeof(phdr);
|
||||
dir_reader->PopEntry();
|
||||
}
|
||||
|
||||
*min_vaddr_ptr = min_vaddr;
|
||||
*dyn_vaddr_ptr = dyn_vaddr;
|
||||
*dyn_count_ptr = dyn_count;
|
||||
sys_close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias,
|
||||
uintptr_t dyn_vaddr,
|
||||
size_t dyn_count) {
|
||||
uintptr_t dyn_addr = load_bias + dyn_vaddr;
|
||||
for (size_t i = 0; i < dyn_count; ++i) {
|
||||
ElfW(Dyn) dyn;
|
||||
CopyFromProcess(&dyn, pid_,
|
||||
reinterpret_cast<const void*>(dyn_addr),
|
||||
sizeof(dyn));
|
||||
if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) {
|
||||
return true;
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
|
||||
// these members are set to -1. Returns true iff all three members are
|
||||
// available.
|
||||
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
||||
assert(info != NULL);
|
||||
char status_path[NAME_MAX];
|
||||
BuildProcPath(status_path, tid, "status");
|
||||
|
||||
const int fd = open(status_path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
LineReader* const line_reader = new(allocator_) LineReader(fd);
|
||||
const char* line;
|
||||
unsigned line_len;
|
||||
|
||||
info->ppid = info->tgid = -1;
|
||||
|
||||
while (line_reader->GetNextLine(&line, &line_len)) {
|
||||
if (my_strncmp("Tgid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->tgid, line + 6);
|
||||
} else if (my_strncmp("PPid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->ppid, line + 6);
|
||||
}
|
||||
dyn_addr += sizeof(dyn);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr,
|
||||
uintptr_t start_addr) {
|
||||
uintptr_t min_vaddr = 0;
|
||||
uintptr_t dyn_vaddr = 0;
|
||||
size_t dyn_count = 0;
|
||||
ParseLoadedElfProgramHeaders(ehdr, start_addr,
|
||||
&min_vaddr, &dyn_vaddr, &dyn_count);
|
||||
// If |min_vaddr| is non-zero and we find Android packed relocation tags,
|
||||
// return the effective load bias.
|
||||
if (min_vaddr != 0) {
|
||||
const uintptr_t load_bias = start_addr - min_vaddr;
|
||||
if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) {
|
||||
return load_bias;
|
||||
line_reader->PopLine(line_len);
|
||||
}
|
||||
|
||||
if (info->ppid == -1 || info->tgid == -1)
|
||||
return false;
|
||||
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__i386)
|
||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
|
||||
if (sys_ptrace(
|
||||
PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*> (offsetof(struct user,
|
||||
u_debugreg[0]) + i *
|
||||
sizeof(debugreg_t)),
|
||||
&info->dregs[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Either |min_vaddr| is zero, or it is non-zero but we did not find the
|
||||
// expected Android packed relocations tags.
|
||||
return start_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LinuxDumper::LatePostprocessMappings() {
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
// Only consider exec mappings that indicate a file path was mapped, and
|
||||
// where the ELF header indicates a mapped shared library.
|
||||
MappingInfo* mapping = mappings_[i];
|
||||
if (!(mapping->exec && mapping->name[0] == '/')) {
|
||||
continue;
|
||||
}
|
||||
ElfW(Ehdr) ehdr;
|
||||
if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) {
|
||||
continue;
|
||||
}
|
||||
if (ehdr.e_type == ET_DYN) {
|
||||
// Compute the effective load bias for this mapped library, and update
|
||||
// the mapping to hold that rather than |start_addr|, at the same time
|
||||
// adjusting |size| to account for the change in |start_addr|. Where
|
||||
// the library does not contain Android packed relocations,
|
||||
// GetEffectiveLoadBias() returns |start_addr| and the mapping entry
|
||||
// is not changed.
|
||||
const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr,
|
||||
mapping->start_addr);
|
||||
mapping->size += mapping->start_addr - load_bias;
|
||||
mapping->start_addr = load_bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
const uint8_t* stack_pointer;
|
||||
#if defined(__i386)
|
||||
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
#endif // __ANDROID__
|
||||
return GetStackInfo(&info->stack, &info->stack_len,
|
||||
(uintptr_t) stack_pointer);
|
||||
}
|
||||
|
||||
// Get information about the stack, given the stack pointer. We don't try to
|
||||
// walk the stack since we might not have all the information needed to do
|
||||
@ -521,8 +489,7 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
const MappingInfo* mapping = FindMapping(stack_pointer);
|
||||
if (!mapping)
|
||||
return false;
|
||||
const ptrdiff_t offset = stack_pointer -
|
||||
reinterpret_cast<uint8_t*>(mapping->start_addr);
|
||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
|
||||
const ptrdiff_t distance_to_end =
|
||||
static_cast<ptrdiff_t>(mapping->size) - offset;
|
||||
*stack_len = distance_to_end > kStackToCapture ?
|
||||
@ -531,6 +498,25 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) {
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
static const size_t word_size = sizeof(tmp);
|
||||
uint8_t* const local = (uint8_t*) dest;
|
||||
uint8_t* const remote = (uint8_t*) src;
|
||||
|
||||
while (done < length) {
|
||||
const size_t l = length - done > word_size ? word_size : length - done;
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in.
|
||||
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
const uintptr_t addr = (uintptr_t) address;
|
||||
@ -559,14 +545,12 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
|
||||
// Check |path| against the /proc/pid/exe 'symlink'.
|
||||
char exe_link[NAME_MAX];
|
||||
if (!BuildProcPath(exe_link, pid_, "exe"))
|
||||
return false;
|
||||
MappingInfo new_mapping = {0};
|
||||
if (!SafeReadLink(exe_link, new_mapping.name))
|
||||
return false;
|
||||
char new_path[PATH_MAX];
|
||||
if (!GetMappingAbsolutePath(new_mapping, new_path))
|
||||
char new_path[NAME_MAX];
|
||||
BuildProcPath(exe_link, pid_, "exe");
|
||||
ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX);
|
||||
if (new_path_len <= 0 || new_path_len == NAME_MAX)
|
||||
return false;
|
||||
new_path[new_path_len] = '\0';
|
||||
if (my_strcmp(path, new_path) != 0)
|
||||
return false;
|
||||
|
||||
@ -580,7 +564,7 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
my_memcpy(path, exe_link, NAME_MAX);
|
||||
memcpy(path, exe_link, NAME_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user