Removed breakpad source code
This commit is contained in:
parent
7078f5cd16
commit
7192253626
45
google-breakpad/.gitignore
vendored
45
google-breakpad/.gitignore
vendored
@ -1,45 +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 GYP generated Visual Studio artifacts.
|
|
||||||
*.filters
|
|
||||||
*.sdf
|
|
||||||
*.sln
|
|
||||||
*.suo
|
|
||||||
*.vcproj
|
|
||||||
*.vcxproj
|
|
||||||
|
|
||||||
# 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
|
|
@ -1 +0,0 @@
|
|||||||
opensource@google.com
|
|
@ -1,66 +0,0 @@
|
|||||||
# Copyright 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
|
|
||||||
# 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 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":
|
|
||||||
"http://google-glog.googlecode.com/svn/trunk@97",
|
|
||||||
|
|
||||||
# Testing libraries and utilities.
|
|
||||||
"src/src/testing": "http://googlemock.googlecode.com/svn/trunk@408",
|
|
||||||
"src/src/testing/gtest": "http://googletest.googlecode.com/svn/trunk@615",
|
|
||||||
|
|
||||||
# Protobuf.
|
|
||||||
"src/src/third_party/protobuf/protobuf":
|
|
||||||
"http://protobuf.googlecode.com/svn/trunk@407",
|
|
||||||
|
|
||||||
# GYP project generator.
|
|
||||||
"src/src/tools/gyp": "http://gyp.googlecode.com/svn/trunk@1886",
|
|
||||||
|
|
||||||
# Linux syscall support.
|
|
||||||
"src/src/third_party/lss":
|
|
||||||
"http://linux-syscall-support.googlecode.com/svn/trunk/lss@24",
|
|
||||||
}
|
|
||||||
|
|
||||||
hooks = [
|
|
||||||
{
|
|
||||||
# TODO(chrisha): Fix the GYP files so that they work without
|
|
||||||
# --no-circular-check.
|
|
||||||
"pattern": ".",
|
|
||||||
"action": ["python",
|
|
||||||
"src/src/tools/gyp/gyp_main.py",
|
|
||||||
"--no-circular-check",
|
|
||||||
"src/src/client/windows/breakpad_client.gyp"],
|
|
||||||
},
|
|
||||||
]
|
|
@ -1,370 +0,0 @@
|
|||||||
Installation Instructions
|
|
||||||
*************************
|
|
||||||
|
|
||||||
Copyright (C) 1994-1996, 1999-2002, 2004-2013 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.
|
|
||||||
|
|
||||||
Basic Installation
|
|
||||||
==================
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for
|
|
||||||
various system-dependent variables used during compilation. It uses
|
|
||||||
those values to create a `Makefile' in each directory of the package.
|
|
||||||
It may also create one or more `.h' files containing system-dependent
|
|
||||||
definitions. Finally, it creates a shell script `config.status' that
|
|
||||||
you can run in the future to recreate the current configuration, and a
|
|
||||||
file `config.log' containing compiler output (useful mainly for
|
|
||||||
debugging `configure').
|
|
||||||
|
|
||||||
It can also use an optional file (typically called `config.cache'
|
|
||||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
|
||||||
the results of its tests to speed up reconfiguring. Caching is
|
|
||||||
disabled by default to prevent problems with accidental use of stale
|
|
||||||
cache files.
|
|
||||||
|
|
||||||
If you need to do unusual things to compile the package, please try
|
|
||||||
to figure out how `configure' could check whether to do them, and mail
|
|
||||||
diffs or instructions to the address given in the `README' so they can
|
|
||||||
be considered for the next release. If you are using the cache, and at
|
|
||||||
some point `config.cache' contains results you don't want to keep, you
|
|
||||||
may remove or edit it.
|
|
||||||
|
|
||||||
The file `configure.ac' (or `configure.in') is used to create
|
|
||||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
|
||||||
you want to change it or regenerate `configure' using a newer version
|
|
||||||
of `autoconf'.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Running `configure' might take a while. While running, it prints
|
|
||||||
some messages telling which features it is checking for.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
also a `make maintainer-clean' target, but that is intended mainly
|
|
||||||
for the package's developers. If you use it, you may have to get
|
|
||||||
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.
|
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters
|
|
||||||
by setting variables in the command line or in the environment. Here
|
|
||||||
is an example:
|
|
||||||
|
|
||||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
|
||||||
|
|
||||||
*Note Defining Variables::, for more details.
|
|
||||||
|
|
||||||
Compiling For Multiple Architectures
|
|
||||||
====================================
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
|
||||||
`/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.
|
|
||||||
|
|
||||||
You can specify separate installation prefixes for
|
|
||||||
architecture-specific files and architecture-independent files. If you
|
|
||||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
|
||||||
PREFIX as the prefix for installing programs and libraries.
|
|
||||||
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
|
|
||||||
=================
|
|
||||||
|
|
||||||
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
|
|
||||||
`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
|
|
||||||
`README' should mention any `--enable-' and `--with-' options that the
|
|
||||||
package recognizes.
|
|
||||||
|
|
||||||
For packages that use the X Window System, `configure' can usually
|
|
||||||
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
|
|
||||||
`--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:
|
|
||||||
|
|
||||||
CPU-COMPANY-SYSTEM
|
|
||||||
|
|
||||||
where SYSTEM can have one of these forms:
|
|
||||||
|
|
||||||
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
|
|
||||||
need to know the machine type.
|
|
||||||
|
|
||||||
If you are _building_ compiler tools for cross-compiling, you should
|
|
||||||
use the option `--target=TYPE' to select the type of system they will
|
|
||||||
produce code for.
|
|
||||||
|
|
||||||
If you want to _use_ a cross compiler, that generates code for a
|
|
||||||
platform different from the build platform, you should specify the
|
|
||||||
"host" platform (i.e., that on which the generated programs will
|
|
||||||
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'.
|
|
||||||
`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.
|
|
||||||
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
|
|
||||||
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
|
|
||||||
them in the `configure' command line, using `VAR=value'. For example:
|
|
||||||
|
|
||||||
./configure CC=/usr/local2/bin/gcc
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
|
||||||
|
|
||||||
`configure' Invocation
|
|
||||||
======================
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
`--version'
|
|
||||||
`-V'
|
|
||||||
Print the version of Autoconf used to generate the `configure'
|
|
||||||
script, and exit.
|
|
||||||
|
|
||||||
`--cache-file=FILE'
|
|
||||||
Enable the cache: use and save the results of the tests in FILE,
|
|
||||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
|
||||||
disable caching.
|
|
||||||
|
|
||||||
`--config-cache'
|
|
||||||
`-C'
|
|
||||||
Alias for `--cache-file=config.cache'.
|
|
||||||
|
|
||||||
`--quiet'
|
|
||||||
`--silent'
|
|
||||||
`-q'
|
|
||||||
Do not print messages saying which checks are being made. To
|
|
||||||
suppress all normal output, redirect it to `/dev/null' (any error
|
|
||||||
messages will still be shown).
|
|
||||||
|
|
||||||
`--srcdir=DIR'
|
|
||||||
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.
|
|
@ -1,50 +0,0 @@
|
|||||||
Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
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.
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,43 +0,0 @@
|
|||||||
Breakpad is a set of client and server components which implement a
|
|
||||||
crash-reporting system.
|
|
||||||
|
|
||||||
|
|
||||||
-----
|
|
||||||
Getting started in 32-bit mode (from trunk)
|
|
||||||
Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
|
|
||||||
Build: make
|
|
||||||
Test: make check
|
|
||||||
Install: make install
|
|
||||||
|
|
||||||
If you need to reconfigure your build be sure to run "make distclean" first.
|
|
||||||
|
|
||||||
|
|
||||||
-----
|
|
||||||
To request change review:
|
|
||||||
0. Get 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.
|
|
1234
google-breakpad/aclocal.m4
vendored
1234
google-breakpad/aclocal.m4
vendored
File diff suppressed because it is too large
Load Diff
@ -1,347 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
# Wrapper for compilers which do not understand '-c -o'.
|
|
||||||
|
|
||||||
scriptversion=2012-10-14.11; # UTC
|
|
||||||
|
|
||||||
# Copyright (C) 1999-2013 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:
|
|
1431
google-breakpad/autotools/config.guess
vendored
1431
google-breakpad/autotools/config.guess
vendored
File diff suppressed because it is too large
Load Diff
1816
google-breakpad/autotools/config.sub
vendored
1816
google-breakpad/autotools/config.sub
vendored
File diff suppressed because it is too large
Load Diff
@ -1,791 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
# depcomp - compile a program generating dependencies as side-effects
|
|
||||||
|
|
||||||
scriptversion=2013-05-30.07; # UTC
|
|
||||||
|
|
||||||
# Copyright (C) 1999-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.
|
|
||||||
|
|
||||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
|
||||||
|
|
||||||
case $1 in
|
|
||||||
'')
|
|
||||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
|
||||||
exit 1;
|
|
||||||
;;
|
|
||||||
-h | --h*)
|
|
||||||
cat <<\EOF
|
|
||||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
|
||||||
|
|
||||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
|
||||||
as side-effects.
|
|
||||||
|
|
||||||
Environment variables:
|
|
||||||
depmode Dependency tracking mode.
|
|
||||||
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.
|
|
||||||
libtool Whether libtool is used (yes/no).
|
|
||||||
|
|
||||||
Report bugs to <bug-automake@gnu.org>.
|
|
||||||
EOF
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
-v | --v*)
|
|
||||||
echo "depcomp $scriptversion"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
|
||||||
depfile=${depfile-`echo "$object" |
|
|
||||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
|
||||||
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
|
|
||||||
# here, because this file can only contain one case statement.
|
|
||||||
if test "$depmode" = hp; then
|
|
||||||
# HP compiler uses -M and no extra arg.
|
|
||||||
gccflag=-M
|
|
||||||
depmode=gcc
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$depmode" in
|
|
||||||
gcc3)
|
|
||||||
## gcc 3 implements dependency tracking that does exactly what
|
|
||||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
|
||||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
|
||||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
|
||||||
## the command line argument order; so add the flags where they
|
|
||||||
## appear in depend2.am. Note that the slowdown incurred here
|
|
||||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
|
||||||
for arg
|
|
||||||
do
|
|
||||||
case $arg in
|
|
||||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
|
||||||
*) set fnord "$@" "$arg" ;;
|
|
||||||
esac
|
|
||||||
shift # fnord
|
|
||||||
shift # $arg
|
|
||||||
done
|
|
||||||
"$@"
|
|
||||||
stat=$?
|
|
||||||
if test $stat -ne 0; then
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
exit $stat
|
|
||||||
fi
|
|
||||||
mv "$tmpdepfile" "$depfile"
|
|
||||||
;;
|
|
||||||
|
|
||||||
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.
|
|
||||||
## - Using -M directly means running the compiler twice (even worse
|
|
||||||
## than renaming).
|
|
||||||
if test -z "$gccflag"; then
|
|
||||||
gccflag=-MD,
|
|
||||||
fi
|
|
||||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
|
||||||
stat=$?
|
|
||||||
if test $stat -ne 0; then
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
exit $stat
|
|
||||||
fi
|
|
||||||
rm -f "$depfile"
|
|
||||||
echo "$object : \\" > "$depfile"
|
|
||||||
# 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.
|
|
||||||
## 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
|
|
||||||
## 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.
|
|
||||||
## 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"
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
;;
|
|
||||||
|
|
||||||
hp)
|
|
||||||
# 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
|
|
||||||
;;
|
|
||||||
|
|
||||||
sgi)
|
|
||||||
if test "$libtool" = yes; then
|
|
||||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
|
||||||
else
|
|
||||||
"$@" -MDupdate "$tmpdepfile"
|
|
||||||
fi
|
|
||||||
stat=$?
|
|
||||||
if test $stat -ne 0; then
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
exit $stat
|
|
||||||
fi
|
|
||||||
rm -f "$depfile"
|
|
||||||
|
|
||||||
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
|
|
||||||
# dependency line.
|
|
||||||
tr ' ' "$nl" < "$tmpdepfile" \
|
|
||||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
|
||||||
| tr "$nl" ' ' >> "$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"
|
|
||||||
else
|
|
||||||
make_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
|
|
||||||
# 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"
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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"
|
|
||||||
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
|
|
||||||
# foo.o: sub/foo.c sub/foo.h
|
|
||||||
# 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
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
exit $stat
|
|
||||||
fi
|
|
||||||
rm -f "$depfile"
|
|
||||||
# Each line is of the form `foo.o: dependent.h',
|
|
||||||
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
|
|
||||||
# Do two passes, one to just change these to
|
|
||||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
|
||||||
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"
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
;;
|
|
||||||
|
|
||||||
hp2)
|
|
||||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
|
||||||
# compilers, which have integrated preprocessors. The correct option
|
|
||||||
# to use with these is +Maked; it writes dependencies to a file named
|
|
||||||
# '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"
|
|
||||||
if test "$libtool" = yes; then
|
|
||||||
tmpdepfile1=$dir$base.d
|
|
||||||
tmpdepfile2=$dir.libs/$base.d
|
|
||||||
"$@" -Wc,+Maked
|
|
||||||
else
|
|
||||||
tmpdepfile1=$dir$base.d
|
|
||||||
tmpdepfile2=$dir$base.d
|
|
||||||
"$@" +Maked
|
|
||||||
fi
|
|
||||||
stat=$?
|
|
||||||
if test $stat -ne 0; then
|
|
||||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
|
||||||
exit $stat
|
|
||||||
fi
|
|
||||||
|
|
||||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
|
||||||
do
|
|
||||||
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"
|
|
||||||
else
|
|
||||||
make_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"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
stat=$?
|
|
||||||
if test $stat -ne 0; then
|
|
||||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
|
||||||
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
|
|
||||||
;;
|
|
||||||
|
|
||||||
#nosideeffect)
|
|
||||||
# This comment above is used by automake to tell side-effect
|
|
||||||
# dependency tracking mechanisms from slower ones.
|
|
||||||
|
|
||||||
dashmstdout)
|
|
||||||
# Important note: in order to support this mode, a compiler *must*
|
|
||||||
# always write the preprocessed file to stdout, regardless of -o.
|
|
||||||
"$@" || exit $?
|
|
||||||
|
|
||||||
# Remove the call to Libtool.
|
|
||||||
if test "$libtool" = yes; then
|
|
||||||
while test "X$1" != 'X--mode=compile'; do
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove '-o $object'.
|
|
||||||
IFS=" "
|
|
||||||
for arg
|
|
||||||
do
|
|
||||||
case $arg in
|
|
||||||
-o)
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
$object)
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
set fnord "$@" "$arg"
|
|
||||||
shift # fnord
|
|
||||||
shift # $arg
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
test -z "$dashmflag" && dashmflag=-M
|
|
||||||
# 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.
|
|
||||||
"$@" $dashmflag |
|
|
||||||
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$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"
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
;;
|
|
||||||
|
|
||||||
dashXmstdout)
|
|
||||||
# This case only exists to satisfy depend.m4. It is never actually
|
|
||||||
# run, as this mode is specially recognized in the preamble.
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
|
|
||||||
makedepend)
|
|
||||||
"$@" || exit $?
|
|
||||||
# Remove any Libtool call
|
|
||||||
if test "$libtool" = yes; then
|
|
||||||
while test "X$1" != 'X--mode=compile'; do
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
# X makedepend
|
|
||||||
shift
|
|
||||||
cleared=no eat=no
|
|
||||||
for arg
|
|
||||||
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/^.*\././'`
|
|
||||||
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"
|
|
||||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
|
||||||
;;
|
|
||||||
|
|
||||||
cpp)
|
|
||||||
# Important note: in order to support this mode, a compiler *must*
|
|
||||||
# always write the preprocessed file to stdout.
|
|
||||||
"$@" || exit $?
|
|
||||||
|
|
||||||
# Remove the call to Libtool.
|
|
||||||
if test "$libtool" = yes; then
|
|
||||||
while test "X$1" != 'X--mode=compile'; do
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove '-o $object'.
|
|
||||||
IFS=" "
|
|
||||||
for arg
|
|
||||||
do
|
|
||||||
case $arg in
|
|
||||||
-o)
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
$object)
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
set fnord "$@" "$arg"
|
|
||||||
shift # fnord
|
|
||||||
shift # $arg
|
|
||||||
;;
|
|
||||||
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"
|
|
||||||
rm -f "$depfile"
|
|
||||||
echo "$object : \\" > "$depfile"
|
|
||||||
cat < "$tmpdepfile" >> "$depfile"
|
|
||||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
;;
|
|
||||||
|
|
||||||
msvisualcpp)
|
|
||||||
# Important note: in order to support this mode, a compiler *must*
|
|
||||||
# always write the preprocessed file to stdout.
|
|
||||||
"$@" || 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 "$@" "$arg"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
"$@" -E 2>/dev/null |
|
|
||||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$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"
|
|
||||||
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 "$@"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Unknown depmode $depmode" 1>&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
# 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,527 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# install - install a program, script, or datafile
|
|
||||||
|
|
||||||
scriptversion=2011-11-20.07; # UTC
|
|
||||||
|
|
||||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
|
||||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
|
||||||
# following copyright and license.
|
|
||||||
#
|
|
||||||
# Copyright (C) 1994 X Consortium
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to
|
|
||||||
# deal in the Software without restriction, including without limitation the
|
|
||||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
# sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
||||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
|
||||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
#
|
|
||||||
# Except as contained in this notice, the name of the X Consortium shall not
|
|
||||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
|
||||||
# ings in this Software without prior written authorization from the X Consor-
|
|
||||||
# tium.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
# when there is no Makefile.
|
|
||||||
#
|
|
||||||
# This script is compatible with the BSD install script, but was written
|
|
||||||
# from scratch.
|
|
||||||
|
|
||||||
nl='
|
|
||||||
'
|
|
||||||
IFS=" "" $nl"
|
|
||||||
|
|
||||||
# set DOITPROG to echo to test this script
|
|
||||||
|
|
||||||
# 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}
|
|
||||||
|
|
||||||
posix_glob='?'
|
|
||||||
initialize_posix_glob='
|
|
||||||
test "$posix_glob" != "?" || {
|
|
||||||
if (set -f) 2>/dev/null; then
|
|
||||||
posix_glob=
|
|
||||||
else
|
|
||||||
posix_glob=:
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
'
|
|
||||||
|
|
||||||
posix_mkdir=
|
|
||||||
|
|
||||||
# Desired mode of installed file.
|
|
||||||
mode=0755
|
|
||||||
|
|
||||||
chgrpcmd=
|
|
||||||
chmodcmd=$chmodprog
|
|
||||||
chowncmd=
|
|
||||||
mvcmd=$mvprog
|
|
||||||
rmcmd="$rmprog -f"
|
|
||||||
stripcmd=
|
|
||||||
|
|
||||||
src=
|
|
||||||
dst=
|
|
||||||
dir_arg=
|
|
||||||
dst_arg=
|
|
||||||
|
|
||||||
copy_on_change=false
|
|
||||||
no_target_directory=
|
|
||||||
|
|
||||||
usage="\
|
|
||||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
|
||||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
|
||||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
|
||||||
or: $0 [OPTION]... -d DIRECTORIES...
|
|
||||||
|
|
||||||
In the 1st form, copy SRCFILE to DSTFILE.
|
|
||||||
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.
|
|
||||||
|
|
||||||
Environment variables override the default commands:
|
|
||||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
|
||||||
RMPROG STRIPPROG
|
|
||||||
"
|
|
||||||
|
|
||||||
while test $# -ne 0; do
|
|
||||||
case $1 in
|
|
||||||
-c) ;;
|
|
||||||
|
|
||||||
-C) copy_on_change=true;;
|
|
||||||
|
|
||||||
-d) dir_arg=true;;
|
|
||||||
|
|
||||||
-g) chgrpcmd="$chgrpprog $2"
|
|
||||||
shift;;
|
|
||||||
|
|
||||||
--help) echo "$usage"; exit $?;;
|
|
||||||
|
|
||||||
-m) mode=$2
|
|
||||||
case $mode in
|
|
||||||
*' '* | *' '* | *'
|
|
||||||
'* | *'*'* | *'?'* | *'['*)
|
|
||||||
echo "$0: invalid mode: $mode" >&2
|
|
||||||
exit 1;;
|
|
||||||
esac
|
|
||||||
shift;;
|
|
||||||
|
|
||||||
-o) chowncmd="$chownprog $2"
|
|
||||||
shift;;
|
|
||||||
|
|
||||||
-s) stripcmd=$stripprog;;
|
|
||||||
|
|
||||||
-t) dst_arg=$2
|
|
||||||
# Protect names problematic for 'test' and other utilities.
|
|
||||||
case $dst_arg in
|
|
||||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
|
||||||
esac
|
|
||||||
shift;;
|
|
||||||
|
|
||||||
-T) no_target_directory=true;;
|
|
||||||
|
|
||||||
--version) echo "$0 $scriptversion"; exit $?;;
|
|
||||||
|
|
||||||
--) shift
|
|
||||||
break;;
|
|
||||||
|
|
||||||
-*) echo "$0: invalid option: $1" >&2
|
|
||||||
exit 1;;
|
|
||||||
|
|
||||||
*) break;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; 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
|
|
||||||
# $@ is not empty: it contains at least $arg.
|
|
||||||
set fnord "$@" "$dst_arg"
|
|
||||||
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
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $# -eq 0; then
|
|
||||||
if test -z "$dir_arg"; then
|
|
||||||
echo "$0: no input file specified." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# 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
|
|
||||||
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
|
|
||||||
|
|
||||||
# Set umask so as not to create temps with too-generous modes.
|
|
||||||
# However, 'strip' requires both read and write access to temps.
|
|
||||||
case $mode in
|
|
||||||
# Optimize common cases.
|
|
||||||
*644) cp_umask=133;;
|
|
||||||
*755) cp_umask=22;;
|
|
||||||
|
|
||||||
*[0-7])
|
|
||||||
if test -z "$stripcmd"; then
|
|
||||||
u_plus_rw=
|
|
||||||
else
|
|
||||||
u_plus_rw='% 200'
|
|
||||||
fi
|
|
||||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
|
||||||
*)
|
|
||||||
if test -z "$stripcmd"; then
|
|
||||||
u_plus_rw=
|
|
||||||
else
|
|
||||||
u_plus_rw=,u+rw
|
|
||||||
fi
|
|
||||||
cp_umask=$mode$u_plus_rw;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
for src
|
|
||||||
do
|
|
||||||
# Protect names problematic for 'test' and other utilities.
|
|
||||||
case $src in
|
|
||||||
-* | [=\(\)!]) src=./$src;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if test -n "$dir_arg"; then
|
|
||||||
dst=$src
|
|
||||||
dstdir=$dst
|
|
||||||
test -d "$dstdir"
|
|
||||||
dstdir_status=$?
|
|
||||||
else
|
|
||||||
|
|
||||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
|
||||||
# might cause directories to be created, which would be especially bad
|
|
||||||
# if $src (and thus $dsttmp) contains '*'.
|
|
||||||
if test ! -f "$src" && test ! -d "$src"; then
|
|
||||||
echo "$0: $src does not exist." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -z "$dst_arg"; then
|
|
||||||
echo "$0: no destination specified." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
dst=$dst_arg
|
|
||||||
|
|
||||||
# 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 -n "$no_target_directory"; then
|
|
||||||
echo "$0: $dst_arg: Is a directory" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
dstdir=$dst
|
|
||||||
dst=$dstdir/`basename "$src"`
|
|
||||||
dstdir_status=0
|
|
||||||
else
|
|
||||||
# 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
|
|
||||||
fi
|
|
||||||
|
|
||||||
obsolete_mkdir_used=false
|
|
||||||
|
|
||||||
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;;
|
|
||||||
|
|
||||||
*[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
|
|
||||||
|
|
||||||
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;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if
|
|
||||||
$posix_mkdir && (
|
|
||||||
umask $mkdir_umask &&
|
|
||||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
|
||||||
)
|
|
||||||
then :
|
|
||||||
else
|
|
||||||
|
|
||||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
|
||||||
# or it failed possibly due to a race condition. Create the
|
|
||||||
# directory the slow way, step by step, checking for races as we go.
|
|
||||||
|
|
||||||
case $dstdir in
|
|
||||||
/*) prefix='/';;
|
|
||||||
[-=\(\)!]*) prefix='./';;
|
|
||||||
*) prefix='';;
|
|
||||||
esac
|
|
||||||
|
|
||||||
eval "$initialize_posix_glob"
|
|
||||||
|
|
||||||
oIFS=$IFS
|
|
||||||
IFS=/
|
|
||||||
$posix_glob set -f
|
|
||||||
set fnord $dstdir
|
|
||||||
shift
|
|
||||||
$posix_glob set +f
|
|
||||||
IFS=$oIFS
|
|
||||||
|
|
||||||
prefixes=
|
|
||||||
|
|
||||||
for d
|
|
||||||
do
|
|
||||||
test X"$d" = X && 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/
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -n "$dir_arg"; then
|
|
||||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
|
||||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
|
||||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
|
||||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
|
||||||
else
|
|
||||||
|
|
||||||
# Make a couple of temp file names in the proper directory.
|
|
||||||
dsttmp=$dstdir/_inst.$$_
|
|
||||||
rmtmp=$dstdir/_rm.$$_
|
|
||||||
|
|
||||||
# Trap to clean up those temp files at exit.
|
|
||||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
|
||||||
|
|
||||||
# Copy the file name to the temp name.
|
|
||||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
|
||||||
|
|
||||||
# and set any options; do chmod last to preserve setuid bits.
|
|
||||||
#
|
|
||||||
# If any of these fail, we abort the whole thing. If we want to
|
|
||||||
# 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"; } &&
|
|
||||||
|
|
||||||
# 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` &&
|
|
||||||
|
|
||||||
eval "$initialize_posix_glob" &&
|
|
||||||
$posix_glob set -f &&
|
|
||||||
set X $old && old=:$2:$4:$5:$6 &&
|
|
||||||
set X $new && new=:$2:$4:$5:$6 &&
|
|
||||||
$posix_glob 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 ||
|
|
||||||
|
|
||||||
# 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 rename the file to the real destination.
|
|
||||||
$doit $mvcmd "$dsttmp" "$dst"
|
|
||||||
}
|
|
||||||
fi || exit 1
|
|
||||||
|
|
||||||
trap '' 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
# End:
|
|
File diff suppressed because it is too large
Load Diff
@ -1,215 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
# Common wrapper for a few potentially missing GNU programs.
|
|
||||||
|
|
||||||
scriptversion=2013-10-28.13; # UTC
|
|
||||||
|
|
||||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
|
||||||
# Originally written 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
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
if test $# -eq 0; then
|
|
||||||
echo 1>&2 "Try '$0 --help' for more information"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
;;
|
|
||||||
|
|
||||||
-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.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help display this help and exit
|
|
||||||
-v, --version output version information and exit
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Send bug reports to <bug-automake@gnu.org>."
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
|
||||||
echo "missing $scriptversion (GNU Automake)"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
-*)
|
|
||||||
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/>"
|
|
||||||
;;
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
# End:
|
|
@ -1,139 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
# test-driver - basic testsuite driver script.
|
|
||||||
|
|
||||||
scriptversion=2013-07-13.22; # 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 [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
|
|
||||||
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,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 +0,0 @@
|
|||||||
# This file is used by gcl to get repository specific information.
|
|
||||||
CODE_REVIEW_SERVER: breakpad.appspot.com
|
|
||||||
CC_LIST: google-breakpad-dev@googlegroups.com
|
|
||||||
TRY_ON_UPLOAD: False
|
|
||||||
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r=
|
|
8082
google-breakpad/configure
vendored
8082
google-breakpad/configure
vendored
File diff suppressed because it is too large
Load Diff
@ -1,159 +0,0 @@
|
|||||||
# Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
|
|
||||||
AC_PREREQ(2.57)
|
|
||||||
|
|
||||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
|
||||||
dnl Sanity check: the argument is just a file that should exist.
|
|
||||||
AC_CONFIG_SRCDIR(README)
|
|
||||||
AC_CONFIG_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_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_ARG_ENABLE(m32,
|
|
||||||
AS_HELP_STRING([--enable-m32],
|
|
||||||
[Compile/build with -m32]
|
|
||||||
[(default is no)]),
|
|
||||||
[case "${enableval}" in
|
|
||||||
yes)
|
|
||||||
CFLAGS="${CFLAGS} -m32"
|
|
||||||
CXXFLAGS="${CXXFLAGS} -m32"
|
|
||||||
usem32=true
|
|
||||||
;;
|
|
||||||
no)
|
|
||||||
usem32=false
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --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])
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(processor,
|
|
||||||
AS_HELP_STRING([--disable-processor],
|
|
||||||
[Don't build processor library]
|
|
||||||
[(default is no)]),
|
|
||||||
[case "${enableval}" in
|
|
||||||
yes)
|
|
||||||
disable_processor=false
|
|
||||||
;;
|
|
||||||
no)
|
|
||||||
disable_processor=true
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --disable-processor)
|
|
||||||
;;
|
|
||||||
esac],
|
|
||||||
[disable_processor=false])
|
|
||||||
AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue)
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(tools,
|
|
||||||
AS_HELP_STRING([--disable-tools],
|
|
||||||
[Don't build tool binaries]
|
|
||||||
[(default is no)]),
|
|
||||||
[case "${enableval}" in
|
|
||||||
yes)
|
|
||||||
disable_tools=false
|
|
||||||
;;
|
|
||||||
no)
|
|
||||||
disable_tools=true
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --disable-tools)
|
|
||||||
;;
|
|
||||||
esac],
|
|
||||||
[disable_tools=false])
|
|
||||||
AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue)
|
|
||||||
|
|
||||||
if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then
|
|
||||||
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!])
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(selftest,
|
|
||||||
AS_HELP_STRING([--enable-selftest],
|
|
||||||
[Run extra tests with "make check" ]
|
|
||||||
[(may conflict with optimizations) ]
|
|
||||||
[(default is no)]),
|
|
||||||
[case "${enableval}" in
|
|
||||||
yes)
|
|
||||||
selftest=true
|
|
||||||
;;
|
|
||||||
no)
|
|
||||||
selftest=false
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --enable-selftest)
|
|
||||||
;;
|
|
||||||
esac],
|
|
||||||
[selftest=false])
|
|
||||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
|
||||||
|
|
||||||
AC_CONFIG_FILES(m4_flatten([
|
|
||||||
breakpad.pc
|
|
||||||
breakpad-client.pc
|
|
||||||
Makefile
|
|
||||||
]))
|
|
||||||
|
|
||||||
AC_OUTPUT
|
|
@ -1,283 +0,0 @@
|
|||||||
# ===========================================================================
|
|
||||||
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
|
|
||||||
# ===========================================================================
|
|
||||||
#
|
|
||||||
# SYNOPSIS
|
|
||||||
#
|
|
||||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
|
||||||
#
|
|
||||||
# DESCRIPTION
|
|
||||||
#
|
|
||||||
# This macro figures out how to build C programs using POSIX threads. It
|
|
||||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
|
||||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
|
||||||
# flags that are needed. (The user can also force certain compiler
|
|
||||||
# flags/libs to be tested by setting these environment variables.)
|
|
||||||
#
|
|
||||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
|
||||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
|
||||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
|
||||||
#
|
|
||||||
# NOTE: You are assumed to not only compile your program with these flags,
|
|
||||||
# but also link it with them as well. e.g. you should link with
|
|
||||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
|
||||||
#
|
|
||||||
# If you are only building threads programs, you may wish to use these
|
|
||||||
# variables in your default LIBS, CFLAGS, and CC:
|
|
||||||
#
|
|
||||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
|
||||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
|
||||||
# CC="$PTHREAD_CC"
|
|
||||||
#
|
|
||||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
|
||||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
|
||||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
|
||||||
#
|
|
||||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
|
||||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
|
||||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
|
||||||
# will define HAVE_PTHREAD.
|
|
||||||
#
|
|
||||||
# Please let the authors know if this macro fails on any platform, or if
|
|
||||||
# you have any other suggestions or comments. This macro was based on work
|
|
||||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
|
||||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
|
||||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
|
||||||
# grateful for the helpful feedback of numerous users.
|
|
||||||
#
|
|
||||||
# LICENSE
|
|
||||||
#
|
|
||||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
|
||||||
#
|
|
||||||
# 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 3 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.
|
|
||||||
#
|
|
||||||
# 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, the respective Autoconf Macro's copyright owner
|
|
||||||
# gives unlimited permission to copy, distribute and modify the configure
|
|
||||||
# scripts that are the output of Autoconf when processing the Macro. You
|
|
||||||
# need not follow the terms of the GNU General Public License when using
|
|
||||||
# or distributing such scripts, even though portions of the text of the
|
|
||||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
|
||||||
# all other use of the material that constitutes the Autoconf Macro.
|
|
||||||
#
|
|
||||||
# This special exception to the GPL applies to versions of the Autoconf
|
|
||||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
|
||||||
# modified version of the Autoconf Macro, you may extend this special
|
|
||||||
# exception to the GPL to apply to your modified version as well.
|
|
||||||
|
|
||||||
#serial 6
|
|
||||||
|
|
||||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
|
||||||
AC_DEFUN([AX_PTHREAD], [
|
|
||||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
|
||||||
AC_LANG_SAVE
|
|
||||||
AC_LANG_C
|
|
||||||
ax_pthread_ok=no
|
|
||||||
|
|
||||||
# We used to check for pthread.h first, but this fails if pthread.h
|
|
||||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
|
||||||
# It gets checked for in the link test anyway.
|
|
||||||
|
|
||||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
|
||||||
# etcetera environment variables, and if threads linking works using
|
|
||||||
# them:
|
|
||||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
|
||||||
save_CFLAGS="$CFLAGS"
|
|
||||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
|
||||||
save_LIBS="$LIBS"
|
|
||||||
LIBS="$PTHREAD_LIBS $LIBS"
|
|
||||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
|
||||||
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
|
|
||||||
AC_MSG_RESULT($ax_pthread_ok)
|
|
||||||
if test x"$ax_pthread_ok" = xno; then
|
|
||||||
PTHREAD_LIBS=""
|
|
||||||
PTHREAD_CFLAGS=""
|
|
||||||
fi
|
|
||||||
LIBS="$save_LIBS"
|
|
||||||
CFLAGS="$save_CFLAGS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# We must check for the threads library under a number of different
|
|
||||||
# names; the ordering is very important because some systems
|
|
||||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
|
||||||
# libraries is broken (non-POSIX).
|
|
||||||
|
|
||||||
# Create a list of thread flags to try. Items starting with a "-" are
|
|
||||||
# C compiler flags, and other items are library names, except for "none"
|
|
||||||
# which indicates that we try without any flags at all, and "pthread-config"
|
|
||||||
# which is a program returning the flags for the Pth emulation library.
|
|
||||||
|
|
||||||
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
|
||||||
|
|
||||||
# The ordering *is* (sometimes) important. Some notes on the
|
|
||||||
# individual items follow:
|
|
||||||
|
|
||||||
# pthreads: AIX (must check this before -lpthread)
|
|
||||||
# none: in case threads are in libc; should be tried before -Kthread and
|
|
||||||
# other compiler flags to prevent continual compiler warnings
|
|
||||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
|
||||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
|
||||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
|
||||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
|
||||||
# -pthreads: Solaris/gcc
|
|
||||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
|
||||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
|
||||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
|
||||||
# also defines -D_REENTRANT)
|
|
||||||
# ... -mt is also the pthreads flag for HP/aCC
|
|
||||||
# pthread: Linux, etcetera
|
|
||||||
# --thread-safe: KAI C++
|
|
||||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
|
||||||
|
|
||||||
case "${host_cpu}-${host_os}" in
|
|
||||||
*solaris*)
|
|
||||||
|
|
||||||
# On Solaris (at least, for some versions), libc contains stubbed
|
|
||||||
# (non-functional) versions of the pthreads routines, so link-based
|
|
||||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
|
||||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
|
||||||
# a function called by this macro, so we could check for that, but
|
|
||||||
# who knows whether they'll stub that too in a future libc.) So,
|
|
||||||
# we'll just look for -pthreads and -lpthread first:
|
|
||||||
|
|
||||||
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*-darwin*)
|
|
||||||
acx_pthread_flags="-pthread $acx_pthread_flags"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if test x"$ax_pthread_ok" = xno; then
|
|
||||||
for flag in $ax_pthread_flags; do
|
|
||||||
|
|
||||||
case $flag in
|
|
||||||
none)
|
|
||||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
|
||||||
;;
|
|
||||||
|
|
||||||
-*)
|
|
||||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
|
||||||
PTHREAD_CFLAGS="$flag"
|
|
||||||
;;
|
|
||||||
|
|
||||||
pthread-config)
|
|
||||||
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
|
|
||||||
if test x"$ax_pthread_config" = xno; then continue; fi
|
|
||||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
|
||||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
|
||||||
PTHREAD_LIBS="-l$flag"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
save_LIBS="$LIBS"
|
|
||||||
save_CFLAGS="$CFLAGS"
|
|
||||||
LIBS="$PTHREAD_LIBS $LIBS"
|
|
||||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
|
||||||
|
|
||||||
# Check for various functions. We must include pthread.h,
|
|
||||||
# since some functions may be macros. (On the Sequent, we
|
|
||||||
# need a special flag -Kthread to make this header compile.)
|
|
||||||
# We check for pthread_join because it is in -lpthread on IRIX
|
|
||||||
# while pthread_create is in libc. We check for pthread_attr_init
|
|
||||||
# due to DEC craziness with -lpthreads. We check for
|
|
||||||
# pthread_cleanup_push because it is one of the few pthread
|
|
||||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
|
||||||
# We try pthread_create on general principles.
|
|
||||||
AC_TRY_LINK([#include <pthread.h>
|
|
||||||
static void routine(void* a) {a=0;}
|
|
||||||
static void* start_routine(void* a) {return a;}],
|
|
||||||
[pthread_t th; pthread_attr_t attr;
|
|
||||||
pthread_join(th, 0);
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
pthread_cleanup_push(routine, 0);
|
|
||||||
pthread_create(&th,0,start_routine,0);
|
|
||||||
pthread_cleanup_pop(0); ],
|
|
||||||
[ax_pthread_ok=yes])
|
|
||||||
|
|
||||||
LIBS="$save_LIBS"
|
|
||||||
CFLAGS="$save_CFLAGS"
|
|
||||||
|
|
||||||
AC_MSG_RESULT($ax_pthread_ok)
|
|
||||||
if test "x$ax_pthread_ok" = xyes; then
|
|
||||||
break;
|
|
||||||
fi
|
|
||||||
|
|
||||||
PTHREAD_LIBS=""
|
|
||||||
PTHREAD_CFLAGS=""
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Various other checks:
|
|
||||||
if test "x$ax_pthread_ok" = xyes; then
|
|
||||||
save_LIBS="$LIBS"
|
|
||||||
LIBS="$PTHREAD_LIBS $LIBS"
|
|
||||||
save_CFLAGS="$CFLAGS"
|
|
||||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
|
||||||
|
|
||||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
|
||||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
|
||||||
attr_name=unknown
|
|
||||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
|
||||||
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
|
|
||||||
[attr_name=$attr; break])
|
|
||||||
done
|
|
||||||
AC_MSG_RESULT($attr_name)
|
|
||||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
|
||||||
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
|
|
||||||
[Define to necessary symbol if this constant
|
|
||||||
uses a non-standard name on your system.])
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
|
||||||
flag=no
|
|
||||||
case "${host_cpu}-${host_os}" in
|
|
||||||
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
|
|
||||||
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
|
|
||||||
esac
|
|
||||||
AC_MSG_RESULT(${flag})
|
|
||||||
if test "x$flag" != xno; then
|
|
||||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
LIBS="$save_LIBS"
|
|
||||||
CFLAGS="$save_CFLAGS"
|
|
||||||
|
|
||||||
# More AIX lossage: must compile with xlc_r or cc_r
|
|
||||||
if test x"$GCC" != xyes; then
|
|
||||||
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
|
|
||||||
else
|
|
||||||
PTHREAD_CC=$CC
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
PTHREAD_CC="$CC"
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_SUBST(PTHREAD_LIBS)
|
|
||||||
AC_SUBST(PTHREAD_CFLAGS)
|
|
||||||
AC_SUBST(PTHREAD_CC)
|
|
||||||
|
|
||||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
|
||||||
if test x"$ax_pthread_ok" = xyes; then
|
|
||||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
|
||||||
:
|
|
||||||
else
|
|
||||||
ax_pthread_ok=no
|
|
||||||
$2
|
|
||||||
fi
|
|
||||||
AC_LANG_RESTORE
|
|
||||||
])dnl AX_PTHREAD
|
|
7377
google-breakpad/m4/libtool.m4
vendored
7377
google-breakpad/m4/libtool.m4
vendored
File diff suppressed because it is too large
Load Diff
368
google-breakpad/m4/ltoptions.m4
vendored
368
google-breakpad/m4/ltoptions.m4
vendored
@ -1,368 +0,0 @@
|
|||||||
# Helper functions for option handling. -*- Autoconf -*-
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
|
||||||
# Written by Gary V. Vaughan, 2004
|
|
||||||
#
|
|
||||||
# This file is free software; the Free Software Foundation gives
|
|
||||||
# unlimited permission to copy and/or distribute it, with or without
|
|
||||||
# modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
# serial 6 ltoptions.m4
|
|
||||||
|
|
||||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
|
||||||
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
|
|
||||||
# ------------------------------------------
|
|
||||||
m4_define([_LT_MANGLE_OPTION],
|
|
||||||
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
|
|
||||||
# ---------------------------------------
|
|
||||||
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
|
|
||||||
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
|
|
||||||
# saved as a flag.
|
|
||||||
m4_define([_LT_SET_OPTION],
|
|
||||||
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
|
|
||||||
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
|
|
||||||
_LT_MANGLE_DEFUN([$1], [$2]),
|
|
||||||
[m4_warning([Unknown $1 option `$2'])])[]dnl
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
|
|
||||||
m4_define([_LT_IF_OPTION],
|
|
||||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
|
|
||||||
# -------------------------------------------------------
|
|
||||||
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
|
|
||||||
# are set.
|
|
||||||
m4_define([_LT_UNLESS_OPTIONS],
|
|
||||||
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
|
||||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
|
|
||||||
[m4_define([$0_found])])])[]dnl
|
|
||||||
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
|
|
||||||
])[]dnl
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
|
|
||||||
# ----------------------------------------
|
|
||||||
# OPTION-LIST is a space-separated list of Libtool options associated
|
|
||||||
# with MACRO-NAME. If any OPTION has a matching handler declared with
|
|
||||||
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
|
|
||||||
# the unknown option and exit.
|
|
||||||
m4_defun([_LT_SET_OPTIONS],
|
|
||||||
[# Set options
|
|
||||||
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
|
||||||
[_LT_SET_OPTION([$1], _LT_Option)])
|
|
||||||
|
|
||||||
m4_if([$1],[LT_INIT],[
|
|
||||||
dnl
|
|
||||||
dnl Simply set some default values (i.e off) if boolean options were not
|
|
||||||
dnl specified:
|
|
||||||
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
|
|
||||||
])
|
|
||||||
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
|
|
||||||
])
|
|
||||||
dnl
|
|
||||||
dnl If no reference was made to various pairs of opposing options, then
|
|
||||||
dnl we run the default mode handler for the pair. For example, if neither
|
|
||||||
dnl `shared' nor `disable-shared' was passed, we enable building of shared
|
|
||||||
dnl archives by default:
|
|
||||||
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
|
|
||||||
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
|
|
||||||
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
|
|
||||||
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
|
|
||||||
[_LT_ENABLE_FAST_INSTALL])
|
|
||||||
])
|
|
||||||
])# _LT_SET_OPTIONS
|
|
||||||
|
|
||||||
|
|
||||||
## --------------------------------- ##
|
|
||||||
## Macros to handle LT_INIT options. ##
|
|
||||||
## --------------------------------- ##
|
|
||||||
|
|
||||||
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
|
|
||||||
# -----------------------------------------
|
|
||||||
m4_define([_LT_MANGLE_DEFUN],
|
|
||||||
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
|
|
||||||
|
|
||||||
|
|
||||||
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
|
|
||||||
# -----------------------------------------------
|
|
||||||
m4_define([LT_OPTION_DEFINE],
|
|
||||||
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
|
|
||||||
])# LT_OPTION_DEFINE
|
|
||||||
|
|
||||||
|
|
||||||
# dlopen
|
|
||||||
# ------
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
|
|
||||||
])
|
|
||||||
|
|
||||||
AU_DEFUN([AC_LIBTOOL_DLOPEN],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], [dlopen])
|
|
||||||
AC_DIAGNOSE([obsolete],
|
|
||||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
|
||||||
put the `dlopen' option into LT_INIT's first parameter.])
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl aclocal-1.4 backwards compatibility:
|
|
||||||
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
|
|
||||||
|
|
||||||
|
|
||||||
# win32-dll
|
|
||||||
# ---------
|
|
||||||
# Declare package support for building win32 dll's.
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
|
|
||||||
[enable_win32_dll=yes
|
|
||||||
|
|
||||||
case $host in
|
|
||||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
|
|
||||||
AC_CHECK_TOOL(AS, as, false)
|
|
||||||
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
|
|
||||||
AC_CHECK_TOOL(OBJDUMP, objdump, false)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
test -z "$AS" && AS=as
|
|
||||||
_LT_DECL([], [AS], [0], [Assembler program])dnl
|
|
||||||
|
|
||||||
test -z "$DLLTOOL" && DLLTOOL=dlltool
|
|
||||||
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
|
|
||||||
|
|
||||||
test -z "$OBJDUMP" && OBJDUMP=objdump
|
|
||||||
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
|
|
||||||
])# win32-dll
|
|
||||||
|
|
||||||
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
|
|
||||||
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
|
||||||
_LT_SET_OPTION([LT_INIT], [win32-dll])
|
|
||||||
AC_DIAGNOSE([obsolete],
|
|
||||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
|
||||||
put the `win32-dll' option into LT_INIT's first parameter.])
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl aclocal-1.4 backwards compatibility:
|
|
||||||
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_ENABLE_SHARED([DEFAULT])
|
|
||||||
# ----------------------------
|
|
||||||
# implement the --enable-shared flag, and supports the `shared' and
|
|
||||||
# `disable-shared' LT_INIT options.
|
|
||||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
|
||||||
m4_define([_LT_ENABLE_SHARED],
|
|
||||||
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
|
||||||
AC_ARG_ENABLE([shared],
|
|
||||||
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
|
|
||||||
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
|
|
||||||
[p=${PACKAGE-default}
|
|
||||||
case $enableval in
|
|
||||||
yes) enable_shared=yes ;;
|
|
||||||
no) enable_shared=no ;;
|
|
||||||
*)
|
|
||||||
enable_shared=no
|
|
||||||
# Look at the argument we got. We use all the common list separators.
|
|
||||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
|
||||||
for pkg in $enableval; do
|
|
||||||
IFS="$lt_save_ifs"
|
|
||||||
if test "X$pkg" = "X$p"; then
|
|
||||||
enable_shared=yes
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
IFS="$lt_save_ifs"
|
|
||||||
;;
|
|
||||||
esac],
|
|
||||||
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
|
|
||||||
|
|
||||||
_LT_DECL([build_libtool_libs], [enable_shared], [0],
|
|
||||||
[Whether or not to build shared libraries])
|
|
||||||
])# _LT_ENABLE_SHARED
|
|
||||||
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
|
|
||||||
|
|
||||||
# Old names:
|
|
||||||
AC_DEFUN([AC_ENABLE_SHARED],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_DEFUN([AC_DISABLE_SHARED],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], [disable-shared])
|
|
||||||
])
|
|
||||||
|
|
||||||
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
|
|
||||||
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
|
|
||||||
|
|
||||||
dnl aclocal-1.4 backwards compatibility:
|
|
||||||
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
|
|
||||||
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_ENABLE_STATIC([DEFAULT])
|
|
||||||
# ----------------------------
|
|
||||||
# implement the --enable-static flag, and support the `static' and
|
|
||||||
# `disable-static' LT_INIT options.
|
|
||||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
|
||||||
m4_define([_LT_ENABLE_STATIC],
|
|
||||||
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
|
||||||
AC_ARG_ENABLE([static],
|
|
||||||
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
|
|
||||||
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
|
|
||||||
[p=${PACKAGE-default}
|
|
||||||
case $enableval in
|
|
||||||
yes) enable_static=yes ;;
|
|
||||||
no) enable_static=no ;;
|
|
||||||
*)
|
|
||||||
enable_static=no
|
|
||||||
# Look at the argument we got. We use all the common list separators.
|
|
||||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
|
||||||
for pkg in $enableval; do
|
|
||||||
IFS="$lt_save_ifs"
|
|
||||||
if test "X$pkg" = "X$p"; then
|
|
||||||
enable_static=yes
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
IFS="$lt_save_ifs"
|
|
||||||
;;
|
|
||||||
esac],
|
|
||||||
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
|
|
||||||
|
|
||||||
_LT_DECL([build_old_libs], [enable_static], [0],
|
|
||||||
[Whether or not to build static libraries])
|
|
||||||
])# _LT_ENABLE_STATIC
|
|
||||||
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
|
|
||||||
|
|
||||||
# Old names:
|
|
||||||
AC_DEFUN([AC_ENABLE_STATIC],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_DEFUN([AC_DISABLE_STATIC],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], [disable-static])
|
|
||||||
])
|
|
||||||
|
|
||||||
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
|
|
||||||
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
|
|
||||||
|
|
||||||
dnl aclocal-1.4 backwards compatibility:
|
|
||||||
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
|
|
||||||
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
|
|
||||||
# ----------------------------------
|
|
||||||
# implement the --enable-fast-install flag, and support the `fast-install'
|
|
||||||
# and `disable-fast-install' LT_INIT options.
|
|
||||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
|
||||||
m4_define([_LT_ENABLE_FAST_INSTALL],
|
|
||||||
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
|
||||||
AC_ARG_ENABLE([fast-install],
|
|
||||||
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
|
|
||||||
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
|
|
||||||
[p=${PACKAGE-default}
|
|
||||||
case $enableval in
|
|
||||||
yes) enable_fast_install=yes ;;
|
|
||||||
no) enable_fast_install=no ;;
|
|
||||||
*)
|
|
||||||
enable_fast_install=no
|
|
||||||
# Look at the argument we got. We use all the common list separators.
|
|
||||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
|
||||||
for pkg in $enableval; do
|
|
||||||
IFS="$lt_save_ifs"
|
|
||||||
if test "X$pkg" = "X$p"; then
|
|
||||||
enable_fast_install=yes
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
IFS="$lt_save_ifs"
|
|
||||||
;;
|
|
||||||
esac],
|
|
||||||
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
|
|
||||||
|
|
||||||
_LT_DECL([fast_install], [enable_fast_install], [0],
|
|
||||||
[Whether or not to optimize for fast installation])dnl
|
|
||||||
])# _LT_ENABLE_FAST_INSTALL
|
|
||||||
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
|
|
||||||
|
|
||||||
# Old names:
|
|
||||||
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
|
|
||||||
AC_DIAGNOSE([obsolete],
|
|
||||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
|
||||||
the `fast-install' option into LT_INIT's first parameter.])
|
|
||||||
])
|
|
||||||
|
|
||||||
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
|
|
||||||
AC_DIAGNOSE([obsolete],
|
|
||||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
|
||||||
the `disable-fast-install' option into LT_INIT's first parameter.])
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl aclocal-1.4 backwards compatibility:
|
|
||||||
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
|
|
||||||
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
|
|
||||||
|
|
||||||
|
|
||||||
# _LT_WITH_PIC([MODE])
|
|
||||||
# --------------------
|
|
||||||
# implement the --with-pic flag, and support the `pic-only' and `no-pic'
|
|
||||||
# LT_INIT options.
|
|
||||||
# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
|
|
||||||
m4_define([_LT_WITH_PIC],
|
|
||||||
[AC_ARG_WITH([pic],
|
|
||||||
[AS_HELP_STRING([--with-pic],
|
|
||||||
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
|
|
||||||
[pic_mode="$withval"],
|
|
||||||
[pic_mode=default])
|
|
||||||
|
|
||||||
test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
|
|
||||||
|
|
||||||
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
|
|
||||||
])# _LT_WITH_PIC
|
|
||||||
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
|
|
||||||
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
|
|
||||||
|
|
||||||
# Old name:
|
|
||||||
AU_DEFUN([AC_LIBTOOL_PICMODE],
|
|
||||||
[_LT_SET_OPTION([LT_INIT], [pic-only])
|
|
||||||
AC_DIAGNOSE([obsolete],
|
|
||||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
|
||||||
put the `pic-only' option into LT_INIT's first parameter.])
|
|
||||||
])
|
|
||||||
|
|
||||||
dnl aclocal-1.4 backwards compatibility:
|
|
||||||
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
|
|
||||||
|
|
||||||
## ----------------- ##
|
|
||||||
## LTDL_INIT Options ##
|
|
||||||
## ----------------- ##
|
|
||||||
|
|
||||||
m4_define([_LTDL_MODE], [])
|
|
||||||
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
|
|
||||||
[m4_define([_LTDL_MODE], [nonrecursive])])
|
|
||||||
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
|
|
||||||
[m4_define([_LTDL_MODE], [recursive])])
|
|
||||||
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
|
|
||||||
[m4_define([_LTDL_MODE], [subproject])])
|
|
||||||
|
|
||||||
m4_define([_LTDL_TYPE], [])
|
|
||||||
LT_OPTION_DEFINE([LTDL_INIT], [installable],
|
|
||||||
[m4_define([_LTDL_TYPE], [installable])])
|
|
||||||
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
|
|
||||||
[m4_define([_LTDL_TYPE], [convenience])])
|
|
123
google-breakpad/m4/ltsugar.m4
vendored
123
google-breakpad/m4/ltsugar.m4
vendored
@ -1,123 +0,0 @@
|
|||||||
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
|
||||||
# Written by Gary V. Vaughan, 2004
|
|
||||||
#
|
|
||||||
# This file is free software; the Free Software Foundation gives
|
|
||||||
# unlimited permission to copy and/or distribute it, with or without
|
|
||||||
# modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
# serial 6 ltsugar.m4
|
|
||||||
|
|
||||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
|
||||||
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_join(SEP, ARG1, [ARG2...])
|
|
||||||
# -----------------------------
|
|
||||||
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
|
|
||||||
# associated separator.
|
|
||||||
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
|
|
||||||
# versions in m4sugar had bugs.
|
|
||||||
m4_define([lt_join],
|
|
||||||
[m4_if([$#], [1], [],
|
|
||||||
[$#], [2], [[$2]],
|
|
||||||
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
|
|
||||||
m4_define([_lt_join],
|
|
||||||
[m4_if([$#$2], [2], [],
|
|
||||||
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_car(LIST)
|
|
||||||
# lt_cdr(LIST)
|
|
||||||
# ------------
|
|
||||||
# Manipulate m4 lists.
|
|
||||||
# These macros are necessary as long as will still need to support
|
|
||||||
# Autoconf-2.59 which quotes differently.
|
|
||||||
m4_define([lt_car], [[$1]])
|
|
||||||
m4_define([lt_cdr],
|
|
||||||
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
|
|
||||||
[$#], 1, [],
|
|
||||||
[m4_dquote(m4_shift($@))])])
|
|
||||||
m4_define([lt_unquote], $1)
|
|
||||||
|
|
||||||
|
|
||||||
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
|
|
||||||
# ------------------------------------------
|
|
||||||
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
|
|
||||||
# Note that neither SEPARATOR nor STRING are expanded; they are appended
|
|
||||||
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
|
|
||||||
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
|
|
||||||
# than defined and empty).
|
|
||||||
#
|
|
||||||
# This macro is needed until we can rely on Autoconf 2.62, since earlier
|
|
||||||
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
|
|
||||||
m4_define([lt_append],
|
|
||||||
[m4_define([$1],
|
|
||||||
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
# Produce a SEP delimited list of all paired combinations of elements of
|
|
||||||
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
|
|
||||||
# has the form PREFIXmINFIXSUFFIXn.
|
|
||||||
# Needed until we can rely on m4_combine added in Autoconf 2.62.
|
|
||||||
m4_define([lt_combine],
|
|
||||||
[m4_if(m4_eval([$# > 3]), [1],
|
|
||||||
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
|
|
||||||
[[m4_foreach([_Lt_prefix], [$2],
|
|
||||||
[m4_foreach([_Lt_suffix],
|
|
||||||
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
|
|
||||||
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
|
|
||||||
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
|
|
||||||
m4_define([lt_if_append_uniq],
|
|
||||||
[m4_ifdef([$1],
|
|
||||||
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
|
|
||||||
[lt_append([$1], [$2], [$3])$4],
|
|
||||||
[$5])],
|
|
||||||
[lt_append([$1], [$2], [$3])$4])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_dict_add(DICT, KEY, VALUE)
|
|
||||||
# -----------------------------
|
|
||||||
m4_define([lt_dict_add],
|
|
||||||
[m4_define([$1($2)], [$3])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
|
|
||||||
# --------------------------------------------
|
|
||||||
m4_define([lt_dict_add_subkey],
|
|
||||||
[m4_define([$1($2:$3)], [$4])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_dict_fetch(DICT, KEY, [SUBKEY])
|
|
||||||
# ----------------------------------
|
|
||||||
m4_define([lt_dict_fetch],
|
|
||||||
[m4_ifval([$3],
|
|
||||||
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
|
|
||||||
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
|
|
||||||
# -----------------------------------------------------------------
|
|
||||||
m4_define([lt_if_dict_fetch],
|
|
||||||
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
|
|
||||||
[$5],
|
|
||||||
[$6])])
|
|
||||||
|
|
||||||
|
|
||||||
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
m4_define([lt_dict_filter],
|
|
||||||
[m4_if([$5], [], [],
|
|
||||||
[lt_join(m4_quote(m4_default([$4], [[, ]])),
|
|
||||||
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
|
|
||||||
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
|
|
||||||
])
|
|
23
google-breakpad/m4/ltversion.m4
vendored
23
google-breakpad/m4/ltversion.m4
vendored
@ -1,23 +0,0 @@
|
|||||||
# ltversion.m4 -- version numbers -*- Autoconf -*-
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004 Free Software Foundation, Inc.
|
|
||||||
# Written by Scott James Remnant, 2004
|
|
||||||
#
|
|
||||||
# This file is free software; the Free Software Foundation gives
|
|
||||||
# unlimited permission to copy and/or distribute it, with or without
|
|
||||||
# modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
# Generated from ltversion.in.
|
|
||||||
|
|
||||||
# serial 3017 ltversion.m4
|
|
||||||
# This file is part of GNU Libtool
|
|
||||||
|
|
||||||
m4_define([LT_PACKAGE_VERSION], [2.2.6b])
|
|
||||||
m4_define([LT_PACKAGE_REVISION], [1.3017])
|
|
||||||
|
|
||||||
AC_DEFUN([LTVERSION_VERSION],
|
|
||||||
[macro_version='2.2.6b'
|
|
||||||
macro_revision='1.3017'
|
|
||||||
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
|
|
||||||
_LT_DECL(, macro_revision, 0)
|
|
||||||
])
|
|
92
google-breakpad/m4/lt~obsolete.m4
vendored
92
google-breakpad/m4/lt~obsolete.m4
vendored
@ -1,92 +0,0 @@
|
|||||||
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
|
|
||||||
#
|
|
||||||
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
|
|
||||||
# Written by Scott James Remnant, 2004.
|
|
||||||
#
|
|
||||||
# This file is free software; the Free Software Foundation gives
|
|
||||||
# unlimited permission to copy and/or distribute it, with or without
|
|
||||||
# modifications, as long as this notice is preserved.
|
|
||||||
|
|
||||||
# serial 4 lt~obsolete.m4
|
|
||||||
|
|
||||||
# These exist entirely to fool aclocal when bootstrapping libtool.
|
|
||||||
#
|
|
||||||
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
|
|
||||||
# which have later been changed to m4_define as they aren't part of the
|
|
||||||
# exported API, or moved to Autoconf or Automake where they belong.
|
|
||||||
#
|
|
||||||
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
|
|
||||||
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
|
|
||||||
# using a macro with the same name in our local m4/libtool.m4 it'll
|
|
||||||
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
|
|
||||||
# and doesn't know about Autoconf macros at all.)
|
|
||||||
#
|
|
||||||
# So we provide this file, which has a silly filename so it's always
|
|
||||||
# included after everything else. This provides aclocal with the
|
|
||||||
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
|
|
||||||
# because those macros already exist, or will be overwritten later.
|
|
||||||
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
|
|
||||||
#
|
|
||||||
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
|
|
||||||
# Yes, that means every name once taken will need to remain here until
|
|
||||||
# we give up compatibility with versions before 1.7, at which point
|
|
||||||
# we need to keep only those names which we still refer to.
|
|
||||||
|
|
||||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
|
||||||
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
|
|
||||||
|
|
||||||
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
|
|
||||||
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
|
|
||||||
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
|
|
||||||
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
|
|
||||||
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
|
|
||||||
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
|
|
||||||
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
|
|
||||||
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
|
|
||||||
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
|
|
||||||
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
|
|
||||||
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
|
|
||||||
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
|
|
||||||
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
|
|
||||||
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
|
|
||||||
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
|
|
||||||
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
|
|
||||||
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
|
|
||||||
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
|
|
||||||
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
|
|
||||||
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
|
|
||||||
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
|
|
||||||
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
|
|
||||||
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
|
|
||||||
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
|
|
||||||
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
|
|
||||||
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
|
|
||||||
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
|
|
||||||
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
|
|
||||||
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
|
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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 BREAKPAD_GOOGLETEST_INCLUDES_H__
|
|
||||||
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
|
|
||||||
|
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
|
||||||
#include "testing/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
|
|
||||||
|
|
||||||
#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:*',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -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,53 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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_CRASH_GENERATION_CLIENT_INFO_H_
|
|
||||||
#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
|
|
||||||
|
|
||||||
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:
|
|
||||||
CrashGenerationServer* crash_server_;
|
|
||||||
pid_t pid_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
|
|
@ -1,105 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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/crash_generation/crash_generation_client.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "common/linux/eintr_wrapper.h"
|
|
||||||
#include "common/linux/ignore_ret.h"
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class CrashGenerationClientImpl : public CrashGenerationClient {
|
|
||||||
public:
|
|
||||||
explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {}
|
|
||||||
virtual ~CrashGenerationClientImpl() {}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
struct kernel_iovec iov;
|
|
||||||
iov.iov_base = const_cast<void*>(blob);
|
|
||||||
iov.iov_len = blob_size;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,65 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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_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() {}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Returns 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);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
@ -1,330 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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 <assert.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "client/linux/crash_generation/crash_generation_server.h"
|
|
||||||
#include "client/linux/crash_generation/client_info.h"
|
|
||||||
#include "client/linux/handler/exception_handler.h"
|
|
||||||
#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';
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer(
|
|
||||||
const int listen_fd,
|
|
||||||
OnClientDumpRequestCallback dump_callback,
|
|
||||||
void* dump_context,
|
|
||||||
OnClientExitingCallback exit_callback,
|
|
||||||
void* exit_context,
|
|
||||||
bool generate_dumps,
|
|
||||||
const string* dump_path) :
|
|
||||||
server_fd_(listen_fd),
|
|
||||||
dump_callback_(dump_callback),
|
|
||||||
dump_context_(dump_context),
|
|
||||||
exit_callback_(exit_callback),
|
|
||||||
exit_context_(exit_context),
|
|
||||||
generate_dumps_(generate_dumps),
|
|
||||||
started_(false)
|
|
||||||
{
|
|
||||||
if (dump_path)
|
|
||||||
dump_dir_ = *dump_path;
|
|
||||||
else
|
|
||||||
dump_dir_ = "/tmp";
|
|
||||||
}
|
|
||||||
|
|
||||||
CrashGenerationServer::~CrashGenerationServer()
|
|
||||||
{
|
|
||||||
if (started_)
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CrashGenerationServer::Start()
|
|
||||||
{
|
|
||||||
if (started_ || 0 > server_fd_)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int control_pipe[2];
|
|
||||||
if (pipe(control_pipe))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
|
|
||||||
return false;
|
|
||||||
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
control_pipe_in_ = control_pipe[0];
|
|
||||||
control_pipe_out_ = control_pipe[1];
|
|
||||||
|
|
||||||
if (pthread_create(&thread_, NULL,
|
|
||||||
ThreadMain, reinterpret_cast<void*>(this)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
started_ = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CrashGenerationServer::Stop()
|
|
||||||
{
|
|
||||||
assert(pthread_self() != thread_);
|
|
||||||
|
|
||||||
if (!started_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
|
|
||||||
|
|
||||||
void* dummy;
|
|
||||||
pthread_join(thread_, &dummy);
|
|
||||||
|
|
||||||
started_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
bool
|
|
||||||
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
|
|
||||||
{
|
|
||||||
int fds[2];
|
|
||||||
|
|
||||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
static const int on = 1;
|
|
||||||
// Enable passcred on the server end of the socket
|
|
||||||
if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
|
|
||||||
return false;
|
|
||||||
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*client_fd = fds[0];
|
|
||||||
*server_fd = fds[1];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following methods/functions execute on the server thread
|
|
||||||
|
|
||||||
void
|
|
||||||
CrashGenerationServer::Run()
|
|
||||||
{
|
|
||||||
struct pollfd pollfds[2];
|
|
||||||
memset(&pollfds, 0, sizeof(pollfds));
|
|
||||||
|
|
||||||
pollfds[0].fd = server_fd_;
|
|
||||||
pollfds[0].events = POLLIN;
|
|
||||||
|
|
||||||
pollfds[1].fd = control_pipe_in_;
|
|
||||||
pollfds[1].events = POLLIN;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// infinite timeout
|
|
||||||
int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
|
|
||||||
if (-1 == nevents) {
|
|
||||||
if (EINTR == errno) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CrashGenerationServer::ClientEvent(short revents)
|
|
||||||
{
|
|
||||||
if (POLLHUP & revents)
|
|
||||||
return false;
|
|
||||||
assert(POLLIN & revents);
|
|
||||||
|
|
||||||
// A process has crashed and has signaled us by writing a datagram
|
|
||||||
// to the death signal socket. The datagram contains the crash context needed
|
|
||||||
// for writing the minidump as well as a file descriptor and a credentials
|
|
||||||
// block so that they can't lie about their pid.
|
|
||||||
|
|
||||||
// The length of the control message:
|
|
||||||
static const unsigned kControlMsgSize =
|
|
||||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
|
|
||||||
// The length of the regular payload:
|
|
||||||
static const unsigned kCrashContextSize =
|
|
||||||
sizeof(google_breakpad::ExceptionHandler::CrashContext);
|
|
||||||
|
|
||||||
struct msghdr msg = {0};
|
|
||||||
struct iovec iov[1];
|
|
||||||
char crash_context[kCrashContextSize];
|
|
||||||
char control[kControlMsgSize];
|
|
||||||
const ssize_t expected_msg_size = sizeof(crash_context);
|
|
||||||
|
|
||||||
iov[0].iov_base = crash_context;
|
|
||||||
iov[0].iov_len = sizeof(crash_context);
|
|
||||||
msg.msg_iov = iov;
|
|
||||||
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
|
|
||||||
msg.msg_control = control;
|
|
||||||
msg.msg_controllen = kControlMsgSize;
|
|
||||||
|
|
||||||
const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
|
|
||||||
if (msg_size != expected_msg_size)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (msg.msg_controllen != kControlMsgSize ||
|
|
||||||
msg.msg_flags & ~MSG_TRUNC)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Walk the control payload and extract the file descriptor and validated pid.
|
|
||||||
pid_t crashing_pid = -1;
|
|
||||||
int signal_fd = -1;
|
|
||||||
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
|
|
||||||
hdr = CMSG_NXTHDR(&msg, hdr)) {
|
|
||||||
if (hdr->cmsg_level != SOL_SOCKET)
|
|
||||||
continue;
|
|
||||||
if (hdr->cmsg_type == SCM_RIGHTS) {
|
|
||||||
const unsigned len = hdr->cmsg_len -
|
|
||||||
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
|
|
||||||
assert(len % sizeof(int) == 0u);
|
|
||||||
const unsigned num_fds = len / sizeof(int);
|
|
||||||
if (num_fds > 1 || num_fds == 0) {
|
|
||||||
// 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]);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
|
|
||||||
}
|
|
||||||
} else if (hdr->cmsg_type == SCM_CREDENTIALS) {
|
|
||||||
const struct ucred *cred =
|
|
||||||
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
|
|
||||||
crashing_pid = cred->pid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crashing_pid == -1 || signal_fd == -1) {
|
|
||||||
if (signal_fd)
|
|
||||||
close(signal_fd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump_callback_) {
|
|
||||||
ClientInfo info(crashing_pid, this);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CrashGenerationServer::ControlEvent(short revents)
|
|
||||||
{
|
|
||||||
if (POLLHUP & revents)
|
|
||||||
return false;
|
|
||||||
assert(POLLIN & revents);
|
|
||||||
|
|
||||||
char command;
|
|
||||||
if (read(control_pipe_in_, &command, 1))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (command) {
|
|
||||||
case kCommandQuit:
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
|
|
||||||
{
|
|
||||||
GUID guid;
|
|
||||||
char guidString[kGUIDStringLength+1];
|
|
||||||
|
|
||||||
if (!(CreateGUID(&guid)
|
|
||||||
&& GUIDToString(&guid, guidString, sizeof(guidString))))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
char path[PATH_MAX];
|
|
||||||
snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
|
|
||||||
|
|
||||||
outFilename = path;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void*
|
|
||||||
CrashGenerationServer::ThreadMain(void *arg)
|
|
||||||
{
|
|
||||||
reinterpret_cast<CrashGenerationServer*>(arg)->Run();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,135 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
|
||||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/using_std_string.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
class ClientInfo;
|
|
||||||
|
|
||||||
class CrashGenerationServer {
|
|
||||||
public:
|
|
||||||
// WARNING: callbacks may be invoked on a different thread
|
|
||||||
// than that which creates the CrashGenerationServer. They must
|
|
||||||
// be thread safe.
|
|
||||||
typedef void (*OnClientDumpRequestCallback)(void* context,
|
|
||||||
const ClientInfo* client_info,
|
|
||||||
const string* file_path);
|
|
||||||
|
|
||||||
typedef void (*OnClientExitingCallback)(void* context,
|
|
||||||
const ClientInfo* client_info);
|
|
||||||
|
|
||||||
// Create an instance with the given parameters.
|
|
||||||
//
|
|
||||||
// Parameter listen_fd: The server fd created by CreateReportChannel().
|
|
||||||
// Parameter dump_callback: Callback for a client crash dump request.
|
|
||||||
// Parameter dump_context: Context for client crash dump request callback.
|
|
||||||
// Parameter exit_callback: Callback for client process exit.
|
|
||||||
// Parameter exit_context: Context for client exit callback.
|
|
||||||
// Parameter generate_dumps: Whether to automatically generate dumps.
|
|
||||||
// Client code of this class might want to generate dumps explicitly
|
|
||||||
// in the crash dump request callback. In that case, false can be
|
|
||||||
// passed for this parameter.
|
|
||||||
// Parameter dump_path: Path for generating dumps; required only if true is
|
|
||||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
|
||||||
CrashGenerationServer(const int listen_fd,
|
|
||||||
OnClientDumpRequestCallback dump_callback,
|
|
||||||
void* dump_context,
|
|
||||||
OnClientExitingCallback exit_callback,
|
|
||||||
void* exit_context,
|
|
||||||
bool generate_dumps,
|
|
||||||
const string* dump_path);
|
|
||||||
|
|
||||||
~CrashGenerationServer();
|
|
||||||
|
|
||||||
// Perform initialization steps needed to start listening to clients.
|
|
||||||
//
|
|
||||||
// Return true if initialization is successful; false otherwise.
|
|
||||||
bool Start();
|
|
||||||
|
|
||||||
// Stop the server.
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
// Create a "channel" that can be used by clients to report crashes
|
|
||||||
// to a CrashGenerationServer. |*server_fd| should be passed to
|
|
||||||
// this class's constructor, and |*client_fd| should be passed to
|
|
||||||
// the ExceptionHandler constructor in the client process.
|
|
||||||
static bool CreateReportChannel(int* server_fd, int* client_fd);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Run the server's event loop
|
|
||||||
void Run();
|
|
||||||
|
|
||||||
// Invoked when an child process (client) event occurs
|
|
||||||
// Returning true => "keep running", false => "exit loop"
|
|
||||||
bool ClientEvent(short revents);
|
|
||||||
|
|
||||||
// Invoked when the controlling thread (main) event occurs
|
|
||||||
// Returning true => "keep running", false => "exit loop"
|
|
||||||
bool ControlEvent(short revents);
|
|
||||||
|
|
||||||
// Return a unique filename at which a minidump can be written
|
|
||||||
bool MakeMinidumpFilename(string& outFilename);
|
|
||||||
|
|
||||||
// Trampoline to |Run()|
|
|
||||||
static void* ThreadMain(void* arg);
|
|
||||||
|
|
||||||
int server_fd_;
|
|
||||||
|
|
||||||
OnClientDumpRequestCallback dump_callback_;
|
|
||||||
void* dump_context_;
|
|
||||||
|
|
||||||
OnClientExitingCallback exit_callback_;
|
|
||||||
void* exit_context_;
|
|
||||||
|
|
||||||
bool generate_dumps_;
|
|
||||||
|
|
||||||
string dump_dir_;
|
|
||||||
|
|
||||||
bool started_;
|
|
||||||
|
|
||||||
pthread_t thread_;
|
|
||||||
int control_pipe_in_;
|
|
||||||
int control_pipe_out_;
|
|
||||||
|
|
||||||
// disable these
|
|
||||||
CrashGenerationServer(const CrashGenerationServer&);
|
|
||||||
CrashGenerationServer& operator=(const CrashGenerationServer&);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
|
@ -1,3 +0,0 @@
|
|||||||
MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so
|
|
||||||
PUBLIC 400 0 __kernel_vsyscall
|
|
||||||
STACK WIN 4 400 100 1 1 0 0 0 0 0 1
|
|
@ -1,3 +0,0 @@
|
|||||||
MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so
|
|
||||||
PUBLIC 400 0 __kernel_vsyscall
|
|
||||||
STACK WIN 4 400 200 3 3 0 0 0 0 0 1
|
|
@ -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,154 +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/seccomp_unwinder.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
void SeccompUnwinder::PopSeccompStackFrame(RawContextCPU* cpu,
|
|
||||||
const MDRawThread& thread,
|
|
||||||
uint8_t* stack_copy) {
|
|
||||||
#if defined(__x86_64)
|
|
||||||
uint64_t bp = cpu->rbp;
|
|
||||||
uint64_t top = thread.stack.start_of_memory_range;
|
|
||||||
for (int i = 4; i--; ) {
|
|
||||||
if (bp < top ||
|
|
||||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
|
||||||
thread.stack.memory.data_size ||
|
|
||||||
bp & 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uint64_t old_top = top;
|
|
||||||
top = bp;
|
|
||||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
|
||||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
|
||||||
if (bp == 0xDEADBEEFDEADBEEFull) {
|
|
||||||
struct {
|
|
||||||
uint64_t r15;
|
|
||||||
uint64_t r14;
|
|
||||||
uint64_t r13;
|
|
||||||
uint64_t r12;
|
|
||||||
uint64_t r11;
|
|
||||||
uint64_t r10;
|
|
||||||
uint64_t r9;
|
|
||||||
uint64_t r8;
|
|
||||||
uint64_t rdi;
|
|
||||||
uint64_t rsi;
|
|
||||||
uint64_t rdx;
|
|
||||||
uint64_t rcx;
|
|
||||||
uint64_t rbx;
|
|
||||||
uint64_t deadbeef;
|
|
||||||
uint64_t rbp;
|
|
||||||
uint64_t fakeret;
|
|
||||||
uint64_t ret;
|
|
||||||
/* char redzone[128]; */
|
|
||||||
} seccomp_stackframe;
|
|
||||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
|
||||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
|
||||||
sizeof(seccomp_stackframe) >
|
|
||||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
my_memcpy(&seccomp_stackframe,
|
|
||||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
|
||||||
sizeof(seccomp_stackframe));
|
|
||||||
cpu->rbx = seccomp_stackframe.rbx;
|
|
||||||
cpu->rcx = seccomp_stackframe.rcx;
|
|
||||||
cpu->rdx = seccomp_stackframe.rdx;
|
|
||||||
cpu->rsi = seccomp_stackframe.rsi;
|
|
||||||
cpu->rdi = seccomp_stackframe.rdi;
|
|
||||||
cpu->rbp = seccomp_stackframe.rbp;
|
|
||||||
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
|
||||||
cpu->r8 = seccomp_stackframe.r8;
|
|
||||||
cpu->r9 = seccomp_stackframe.r9;
|
|
||||||
cpu->r10 = seccomp_stackframe.r10;
|
|
||||||
cpu->r11 = seccomp_stackframe.r11;
|
|
||||||
cpu->r12 = seccomp_stackframe.r12;
|
|
||||||
cpu->r13 = seccomp_stackframe.r13;
|
|
||||||
cpu->r14 = seccomp_stackframe.r14;
|
|
||||||
cpu->r15 = seccomp_stackframe.r15;
|
|
||||||
cpu->rip = seccomp_stackframe.fakeret;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined(__i386__)
|
|
||||||
uint32_t bp = cpu->ebp;
|
|
||||||
uint32_t top = thread.stack.start_of_memory_range;
|
|
||||||
for (int i = 4; i--; ) {
|
|
||||||
if (bp < top ||
|
|
||||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
|
||||||
thread.stack.memory.data_size ||
|
|
||||||
bp & 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uint32_t old_top = top;
|
|
||||||
top = bp;
|
|
||||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
|
||||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
|
||||||
if (bp == 0xDEADBEEFu) {
|
|
||||||
struct {
|
|
||||||
uint32_t edi;
|
|
||||||
uint32_t esi;
|
|
||||||
uint32_t edx;
|
|
||||||
uint32_t ecx;
|
|
||||||
uint32_t ebx;
|
|
||||||
uint32_t deadbeef;
|
|
||||||
uint32_t ebp;
|
|
||||||
uint32_t fakeret;
|
|
||||||
uint32_t ret;
|
|
||||||
} seccomp_stackframe;
|
|
||||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
|
||||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
|
||||||
sizeof(seccomp_stackframe) >
|
|
||||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
my_memcpy(&seccomp_stackframe,
|
|
||||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
|
||||||
sizeof(seccomp_stackframe));
|
|
||||||
cpu->ebx = seccomp_stackframe.ebx;
|
|
||||||
cpu->ecx = seccomp_stackframe.ecx;
|
|
||||||
cpu->edx = seccomp_stackframe.edx;
|
|
||||||
cpu->esi = seccomp_stackframe.esi;
|
|
||||||
cpu->edi = seccomp_stackframe.edi;
|
|
||||||
cpu->ebp = seccomp_stackframe.ebp;
|
|
||||||
cpu->esp = top + 4*sizeof(void*);
|
|
||||||
cpu->eip = seccomp_stackframe.fakeret;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,50 +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_SECCOMP_UNWINDER_H
|
|
||||||
#define CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
|
||||||
|
|
||||||
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
struct SeccompUnwinder {
|
|
||||||
|
|
||||||
// Check if the top of the stack is part of a system call that has been
|
|
||||||
// redirected by the seccomp sandbox. If so, try to pop the stack frames
|
|
||||||
// all the way back to the point where the interception happened.
|
|
||||||
static void PopSeccompStackFrame(RawContextCPU* cpu,
|
|
||||||
const MDRawThread& thread,
|
|
||||||
uint8_t* stack_copy);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
|
@ -1,268 +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 "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;
|
|
||||||
#if defined (__ANDROID__)
|
|
||||||
// Internal bug b/18097559
|
|
||||||
out->flt_save.mx_csr_mask = fpregs.mxcsr_mask;
|
|
||||||
#else
|
|
||||||
out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
|
|
||||||
#endif
|
|
||||||
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 regs.epc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
|
||||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
|
||||||
|
|
||||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
|
||||||
out->iregs[i] = regs.regs[i];
|
|
||||||
|
|
||||||
out->mdhi = regs.hi;
|
|
||||||
out->mdlo = regs.lo;
|
|
||||||
|
|
||||||
for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i) {
|
|
||||||
out->hi[i] = hi[i];
|
|
||||||
out->lo[i] = lo[i];
|
|
||||||
}
|
|
||||||
out->dsp_control = dsp_control;
|
|
||||||
|
|
||||||
out->epc = regs.epc;
|
|
||||||
out->badvaddr = regs.badvaddr;
|
|
||||||
out->status = regs.status;
|
|
||||||
out->cause = regs.cause;
|
|
||||||
|
|
||||||
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
|
||||||
out->float_save.regs[i] = fpregs.regs[i];
|
|
||||||
|
|
||||||
out->float_save.fpcsr = fpregs.fpcsr;
|
|
||||||
out->float_save.fir = fpregs.fir;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,88 +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 <asm/ptrace.h>
|
|
||||||
struct user_pt_regs regs;
|
|
||||||
struct user_fpsimd_state fpregs;
|
|
||||||
#elif defined(__mips__)
|
|
||||||
user_regs_struct regs;
|
|
||||||
user_fpregs_struct fpregs;
|
|
||||||
uint32_t hi[3];
|
|
||||||
uint32_t lo[3];
|
|
||||||
uint32_t dsp_control;
|
|
||||||
#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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
|
@ -1,251 +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) {
|
|
||||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
|
||||||
|
|
||||||
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;
|
|
||||||
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
|
|
||||||
}
|
|
||||||
#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
|
|
@ -1,727 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// The ExceptionHandler object installs signal handlers for a number of
|
|
||||||
// signals. We rely on the signal handler running on the thread which crashed
|
|
||||||
// in order to identify it. This is true of the synchronous signals (SEGV etc),
|
|
||||||
// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
|
|
||||||
// uses ExceptionHandler, you need to use tgkill to direct it to the current
|
|
||||||
// thread.
|
|
||||||
//
|
|
||||||
// The signal flow looks like this:
|
|
||||||
//
|
|
||||||
// SignalHandler (uses a global stack of ExceptionHandler objects to find
|
|
||||||
// | one to handle the signal. If the first rejects it, try
|
|
||||||
// | the second etc...)
|
|
||||||
// V
|
|
||||||
// HandleSignal ----------------------------| (clones a new process which
|
|
||||||
// | | shares an address space with
|
|
||||||
// (wait for cloned | the crashed process. This
|
|
||||||
// process) | allows us to ptrace the crashed
|
|
||||||
// | | process)
|
|
||||||
// V V
|
|
||||||
// (set signal handler to ThreadEntry (static function to bounce
|
|
||||||
// SIG_DFL and rethrow, | back into the object)
|
|
||||||
// killing the crashed |
|
|
||||||
// process) V
|
|
||||||
// DoDump (writes minidump)
|
|
||||||
// |
|
|
||||||
// V
|
|
||||||
// sys_exit
|
|
||||||
//
|
|
||||||
|
|
||||||
// This code is a little fragmented. Different functions of the ExceptionHandler
|
|
||||||
// class run in a number of different contexts. Some of them run in a normal
|
|
||||||
// context and are easy to code, others run in a compromised context and the
|
|
||||||
// restrictions at the top of minidump_writer.cc apply: no libc and use the
|
|
||||||
// alternative malloc. Each function should have comment above it detailing the
|
|
||||||
// context which it runs in.
|
|
||||||
|
|
||||||
#include "client/linux/handler/exception_handler.h"
|
|
||||||
|
|
||||||
#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>
|
|
||||||
|
|
||||||
#include <sys/signal.h>
|
|
||||||
#include <sys/ucontext.h>
|
|
||||||
#include <sys/user.h>
|
|
||||||
#include <ucontext.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/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
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A wrapper for the tgkill syscall: send a signal to a specific thread.
|
|
||||||
static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
|
||||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
|
||||||
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();
|
|
||||||
|
|
||||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
|
||||||
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_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
// static
|
|
||||||
bool ExceptionHandler::InstallHandlersLocked() {
|
|
||||||
if (handlers_installed)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
signal(kExceptionSignals[i], SIG_DFL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handlers_installed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
|
||||||
// crash_handler_ = callback;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// Runs on the crashing thread.
|
|
||||||
// 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_);
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
signal(sig, SIG_DFL);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&g_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
signal(sig, SIG_DFL);
|
|
||||||
} else {
|
|
||||||
RestoreHandlersLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
|
||||||
|
|
||||||
if (info->si_pid || sig == SIGABRT) {
|
|
||||||
// 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.
|
|
||||||
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
|
|
||||||
// result in an incorrect exit code.
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
|
|
||||||
// No need to reissue the signal. It will automatically trigger again,
|
|
||||||
// when we return from the signal handler.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ThreadArgument {
|
|
||||||
pid_t pid; // the crashing process
|
|
||||||
const MinidumpDescriptor* minidump_descriptor;
|
|
||||||
ExceptionHandler* handler;
|
|
||||||
const void* context; // a CrashContext structure
|
|
||||||
size_t context_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is the entry function for the cloned process. We are in a compromised
|
|
||||||
// context here: see the top of the file.
|
|
||||||
// static
|
|
||||||
int ExceptionHandler::ThreadEntry(void *arg) {
|
|
||||||
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
|
|
||||||
|
|
||||||
// Block here until the crashing process unblocks us when
|
|
||||||
// we're allowed to use ptrace
|
|
||||||
thread_arg->handler->WaitForContinueSignal();
|
|
||||||
|
|
||||||
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
|
|
||||||
thread_arg->context_size) == false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// Runs on the crashing thread.
|
|
||||||
bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|
||||||
if (filter_ && !filter_(callback_context_))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Allow ourselves to be dumped if the signal is trusted.
|
|
||||||
bool signal_trusted = info->si_code > 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
CrashContext context;
|
|
||||||
// Fill in all the holes in the struct to make Valgrind happy.
|
|
||||||
memset(&context, 0, sizeof(context));
|
|
||||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
|
||||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
|
||||||
#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(&context.float_state, fp_ptr, sizeof(context.float_state));
|
|
||||||
}
|
|
||||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
|
||||||
// FP state is not part of user ABI on ARM Linux.
|
|
||||||
// In case of MIPS Linux FP state is already part of struct ucontext
|
|
||||||
// and 'float_state' is not a member of CrashContext.
|
|
||||||
struct ucontext *uc_ptr = (struct ucontext*)uc;
|
|
||||||
if (uc_ptr->uc_mcontext.fpregs) {
|
|
||||||
memcpy(&context.float_state,
|
|
||||||
uc_ptr->uc_mcontext.fpregs,
|
|
||||||
sizeof(context.float_state));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
context.tid = syscall(__NR_gettid);
|
|
||||||
if (crash_handler_ != NULL) {
|
|
||||||
if (crash_handler_(&context, sizeof(context), callback_context_)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return GenerateDump(&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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function may run in a compromised context: see the top of the file.
|
|
||||||
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;
|
|
||||||
PageAllocator allocator;
|
|
||||||
uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
|
|
||||||
if (!stack)
|
|
||||||
return false;
|
|
||||||
// clone() needs the top-most address. (scrub just to be safe)
|
|
||||||
stack += kChildStackSize;
|
|
||||||
my_memset(stack - 16, 0, 16);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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. 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) {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the child to ptrace us
|
|
||||||
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
|
||||||
SendContinueSignalToChild();
|
|
||||||
int status;
|
|
||||||
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
|
||||||
if (callback_)
|
|
||||||
success = callback_(minidump_descriptor_, callback_context_, success);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// Runs on the cloned process.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// 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_);
|
|
||||||
}
|
|
||||||
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(),
|
|
||||||
crashing_process,
|
|
||||||
context,
|
|
||||||
context_size,
|
|
||||||
mapping_list_,
|
|
||||||
app_memory_list_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
bool ExceptionHandler::WriteMinidump(const string& dump_path,
|
|
||||||
MinidumpCallback callback,
|
|
||||||
void* callback_context) {
|
|
||||||
MinidumpDescriptor descriptor(dump_path);
|
|
||||||
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
#else
|
|
||||||
#error "This code has not been ported to your platform yet."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return GenerateDump(&context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExceptionHandler::AddMappingInfo(const string& name,
|
|
||||||
const uint8_t identifier[sizeof(MDGUID)],
|
|
||||||
uintptr_t start_address,
|
|
||||||
size_t mapping_size,
|
|
||||||
size_t file_offset) {
|
|
||||||
MappingInfo info;
|
|
||||||
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';
|
|
||||||
|
|
||||||
MappingEntry mapping;
|
|
||||||
mapping.first = info;
|
|
||||||
memcpy(mapping.second, identifier, sizeof(MDGUID));
|
|
||||||
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
|
|
@ -1,278 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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_EXCEPTION_HANDLER_H_
|
|
||||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/ucontext.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#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"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// ExceptionHandler
|
|
||||||
//
|
|
||||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
|
||||||
// or when WriteMinidump() is called explicitly by your program.
|
|
||||||
//
|
|
||||||
// To have the exception handler write minidumps when an uncaught exception
|
|
||||||
// (crash) occurs, you should create an instance early in the execution
|
|
||||||
// of your program, and keep it around for the entire time you want to
|
|
||||||
// have crash handling active (typically, until shutdown).
|
|
||||||
// (NOTE): There should be only be one this kind of exception handler
|
|
||||||
// object per process.
|
|
||||||
//
|
|
||||||
// If you want to write minidumps without installing the exception handler,
|
|
||||||
// you can create an ExceptionHandler with install_handler set to false,
|
|
||||||
// then call WriteMinidump. You can also use this technique if you want to
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
// callback_context when the handler was created.
|
|
||||||
//
|
|
||||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
|
||||||
// attempting to write a minidump. If a FilterCallback returns false,
|
|
||||||
// Breakpad will immediately report the exception as unhandled without
|
|
||||||
// writing a minidump, allowing another handler the opportunity to handle it.
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
// If an exception occurred and the callback returns true, Breakpad will
|
|
||||||
// treat the exception as fully-handled, suppressing any other handlers from
|
|
||||||
// being notified of the exception. If the callback returns false, Breakpad
|
|
||||||
// will treat the exception as unhandled, and allow another handler to handle
|
|
||||||
// it. If there are no other handlers, Breakpad will report the exception to
|
|
||||||
// the system as unhandled, allowing a debugger or native crash dialog the
|
|
||||||
// opportunity to handle the exception. Most callback implementations
|
|
||||||
// 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,
|
|
||||||
bool succeeded);
|
|
||||||
|
|
||||||
// In certain cases, a user may wish to handle the generation of the minidump
|
|
||||||
// themselves. In this case, they can install a handler callback which is
|
|
||||||
// called when a crash has occurred. If this function returns true, no other
|
|
||||||
// processing of occurs and the process will shortly be crashed. If this
|
|
||||||
// returns false, the normal processing continues.
|
|
||||||
typedef bool (*HandlerCallback)(const void* crash_context,
|
|
||||||
size_t crash_context_size,
|
|
||||||
void* context);
|
|
||||||
|
|
||||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
|
||||||
// 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.
|
|
||||||
// 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,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
bool WriteMinidump();
|
|
||||||
|
|
||||||
// Convenience form of WriteMinidump which does not require an
|
|
||||||
// ExceptionHandler instance.
|
|
||||||
static bool WriteMinidump(const 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);
|
|
||||||
|
|
||||||
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
|
|
||||||
// blob. It shouldn't be needed in any user code.
|
|
||||||
struct CrashContext {
|
|
||||||
siginfo_t siginfo;
|
|
||||||
pid_t tid; // the crashing thread.
|
|
||||||
struct ucontext context;
|
|
||||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
|
||||||
// #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;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns whether out-of-process dump generation is used or not.
|
|
||||||
bool IsOutOfProcess() const {
|
|
||||||
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)],
|
|
||||||
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 PreresolveSymbols();
|
|
||||||
bool GenerateDump(CrashContext *context);
|
|
||||||
void SendContinueSignalToChild();
|
|
||||||
void WaitForContinueSignal();
|
|
||||||
|
|
||||||
static void SignalHandler(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);
|
|
||||||
|
|
||||||
const FilterCallback filter_;
|
|
||||||
const MinidumpCallback callback_;
|
|
||||||
void* const callback_context_;
|
|
||||||
|
|
||||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
|
||||||
|
|
||||||
MinidumpDescriptor minidump_descriptor_;
|
|
||||||
|
|
||||||
// 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_;
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// 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
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
|
File diff suppressed because it is too large
Load Diff
@ -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 <stdio.h>
|
|
||||||
|
|
||||||
#include "client/linux/handler/minidump_descriptor.h"
|
|
||||||
|
|
||||||
#include "common/linux/guid_creator.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
//static
|
|
||||||
const MinidumpDescriptor::MicrodumpOnConsole kMicrodumpOnConsole = {};
|
|
||||||
|
|
||||||
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
|
||||||
: mode_(descriptor.mode_),
|
|
||||||
fd_(descriptor.fd_),
|
|
||||||
directory_(descriptor.directory_),
|
|
||||||
c_path_(NULL),
|
|
||||||
size_limit_(descriptor.size_limit_) {
|
|
||||||
// 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_;
|
|
||||||
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,129 +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 "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; }
|
|
||||||
|
|
||||||
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_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
|
@ -1,48 +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>
|
|
||||||
#else
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace logger {
|
|
||||||
|
|
||||||
int write(const char* buf, size_t nbytes) {
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
|
|
||||||
#else
|
|
||||||
return sys_write(2, buf, nbytes);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace logger
|
|
@ -1,41 +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);
|
|
||||||
|
|
||||||
} // namespace logger
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_LOG_LOG_H_
|
|
@ -1,364 +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 <sys/utsname.h>
|
|
||||||
|
|
||||||
#include "client/linux/dump_writer_common/seccomp_unwinder.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/minidump_writer/linux_ptrace_dumper.h"
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
#include "client/linux/log/log.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using google_breakpad::ExceptionHandler;
|
|
||||||
using google_breakpad::LinuxDumper;
|
|
||||||
using google_breakpad::LinuxPtraceDumper;
|
|
||||||
using google_breakpad::MappingInfo;
|
|
||||||
using google_breakpad::MappingList;
|
|
||||||
using google_breakpad::RawContextCPU;
|
|
||||||
using google_breakpad::SeccompUnwinder;
|
|
||||||
using google_breakpad::ThreadInfo;
|
|
||||||
using google_breakpad::UContextReader;
|
|
||||||
|
|
||||||
class MicrodumpWriter {
|
|
||||||
public:
|
|
||||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
|
||||||
const MappingList& mappings,
|
|
||||||
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) { }
|
|
||||||
|
|
||||||
~MicrodumpWriter() { dumper_->ThreadsResume(); }
|
|
||||||
|
|
||||||
bool Init() {
|
|
||||||
if (!dumper_->Init())
|
|
||||||
return false;
|
|
||||||
return dumper_->ThreadsSuspend();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Dump() {
|
|
||||||
bool success;
|
|
||||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
|
||||||
success = DumpOSInformation();
|
|
||||||
if (success)
|
|
||||||
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) {
|
|
||||||
logger::write(msg, my_strlen(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stages the given string in the current line buffer.
|
|
||||||
void LogAppend(const char* str) {
|
|
||||||
my_strlcat(log_line_, str, sizeof(log_line_));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
logger::write(log_line_, my_strlen(log_line_));
|
|
||||||
my_strlcpy(log_line_, "", sizeof(log_line_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DumpOSInformation() {
|
|
||||||
struct utsname uts;
|
|
||||||
if (uname(&uts))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
const char kOSId[] = "A";
|
|
||||||
#else
|
|
||||||
const char kOSId[] = "L";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LogAppend("O ");
|
|
||||||
LogAppend(kOSId);
|
|
||||||
LogAppend(" \"");
|
|
||||||
LogAppend(uts.machine);
|
|
||||||
LogAppend("\" \"");
|
|
||||||
LogAppend(uts.release);
|
|
||||||
LogAppend(" \"");
|
|
||||||
LogAppend(uts.version);
|
|
||||||
LogAppend("\"");
|
|
||||||
LogCommitLine();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
assert(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
if (stack_copy)
|
|
||||||
SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy);
|
|
||||||
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) {
|
|
||||||
MDGUID module_identifier;
|
|
||||||
if (identifier) {
|
|
||||||
// GUID was provided by caller.
|
|
||||||
my_memcpy(&module_identifier, identifier, sizeof(MDGUID));
|
|
||||||
} else {
|
|
||||||
dumper_->ElfFileIdentifierForMapping(
|
|
||||||
mapping,
|
|
||||||
member,
|
|
||||||
mapping_id,
|
|
||||||
reinterpret_cast<uint8_t*>(&module_identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
char file_name[NAME_MAX];
|
|
||||||
char file_path[NAME_MAX];
|
|
||||||
LinuxDumper::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(" ");
|
|
||||||
LogAppend(file_name);
|
|
||||||
LogCommitLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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];
|
|
||||||
// Skip mappings which don't look like libraries.
|
|
||||||
if (!strstr(mapping.name, ".so") || // dump only libs (skip fonts, apks).
|
|
||||||
mapping.size < 4096) { // too small to get a signature for.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (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_;
|
|
||||||
char log_line_[512];
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
bool WriteMicrodump(pid_t crashing_process,
|
|
||||||
const void* blob,
|
|
||||||
size_t blob_size,
|
|
||||||
const MappingList& mappings) {
|
|
||||||
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, &dumper);
|
|
||||||
if (!writer.Init())
|
|
||||||
return false;
|
|
||||||
return writer.Dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,58 +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 {
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// Returns true iff successful.
|
|
||||||
bool WriteMicrodump(pid_t crashing_process,
|
|
||||||
const void* blob,
|
|
||||||
size_t blob_size,
|
|
||||||
const MappingList& mappings);
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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_DIRECTORY_READER_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// A class for enumerating a directory without using diropen/readdir or other
|
|
||||||
// functions which may allocate memory.
|
|
||||||
class DirectoryReader {
|
|
||||||
public:
|
|
||||||
DirectoryReader(int fd)
|
|
||||||
: fd_(fd),
|
|
||||||
buf_used_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the next entry from the directory
|
|
||||||
// name: (output) the NUL terminated entry name
|
|
||||||
//
|
|
||||||
// Returns true iff successful (false on EOF).
|
|
||||||
//
|
|
||||||
// After calling this, one must call |PopEntry| otherwise you'll get the same
|
|
||||||
// entry over and over.
|
|
||||||
bool GetNextEntry(const char** name) {
|
|
||||||
struct kernel_dirent* const dent =
|
|
||||||
reinterpret_cast<kernel_dirent*>(buf_);
|
|
||||||
|
|
||||||
if (buf_used_ == 0) {
|
|
||||||
// need to read more entries.
|
|
||||||
const int n = sys_getdents(fd_, dent, sizeof(buf_));
|
|
||||||
if (n < 0) {
|
|
||||||
return false;
|
|
||||||
} else if (n == 0) {
|
|
||||||
hit_eof_ = true;
|
|
||||||
} else {
|
|
||||||
buf_used_ += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf_used_ == 0 && hit_eof_)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
assert(buf_used_ > 0);
|
|
||||||
|
|
||||||
*name = dent->d_name;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PopEntry() {
|
|
||||||
if (!buf_used_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const struct kernel_dirent* const dent =
|
|
||||||
reinterpret_cast<kernel_dirent*>(buf_);
|
|
||||||
|
|
||||||
buf_used_ -= dent->d_reclen;
|
|
||||||
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int fd_;
|
|
||||||
bool hit_eof_;
|
|
||||||
unsigned buf_used_;
|
|
||||||
uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
|
|
@ -1,78 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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 <set>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#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;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef testing::Test DirectoryReaderTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(DirectoryReaderTest, CompareResults) {
|
|
||||||
std::set<string> dent_set;
|
|
||||||
|
|
||||||
DIR *const dir = opendir("/proc/self");
|
|
||||||
ASSERT_TRUE(dir != NULL);
|
|
||||||
|
|
||||||
struct dirent* dent;
|
|
||||||
while ((dent = readdir(dir)))
|
|
||||||
dent_set.insert(dent->d_name);
|
|
||||||
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
|
|
||||||
ASSERT_GE(fd, 0);
|
|
||||||
|
|
||||||
DirectoryReader dir_reader(fd);
|
|
||||||
unsigned seen = 0;
|
|
||||||
|
|
||||||
const char* name;
|
|
||||||
while (dir_reader.GetNextEntry(&name)) {
|
|
||||||
ASSERT_TRUE(dent_set.find(name) != dent_set.end());
|
|
||||||
seen++;
|
|
||||||
dir_reader.PopEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_TRUE(dent_set.find("status") != dent_set.end());
|
|
||||||
ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
|
|
||||||
ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
|
|
||||||
|
|
||||||
ASSERT_EQ(dent_set.size(), seen);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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_LINE_READER_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_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 {
|
|
||||||
|
|
||||||
// A class for reading a file, line by line, without using fopen/fgets or other
|
|
||||||
// functions which may allocate memory.
|
|
||||||
class LineReader {
|
|
||||||
public:
|
|
||||||
LineReader(int fd)
|
|
||||||
: fd_(fd),
|
|
||||||
hit_eof_(false),
|
|
||||||
buf_used_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// The maximum length of a line.
|
|
||||||
static const size_t kMaxLineLen = 512;
|
|
||||||
|
|
||||||
// Return the next line from the file.
|
|
||||||
// line: (output) a pointer to the start of the line. The line is NUL
|
|
||||||
// terminated.
|
|
||||||
// len: (output) the length of the line (not inc the NUL byte)
|
|
||||||
//
|
|
||||||
// Returns true iff successful (false on EOF).
|
|
||||||
//
|
|
||||||
// One must call |PopLine| after this function, otherwise you'll continue to
|
|
||||||
// get the same line over and over.
|
|
||||||
bool GetNextLine(const char **line, unsigned *len) {
|
|
||||||
for (;;) {
|
|
||||||
if (buf_used_ == 0 && hit_eof_)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < buf_used_; ++i) {
|
|
||||||
if (buf_[i] == '\n' || buf_[i] == 0) {
|
|
||||||
buf_[i] = 0;
|
|
||||||
*len = i;
|
|
||||||
*line = buf_;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf_used_ == sizeof(buf_)) {
|
|
||||||
// we scanned the whole buffer and didn't find an end-of-line marker.
|
|
||||||
// This line is too long to process.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We didn't find any end-of-line terminators in the buffer. However, if
|
|
||||||
// this is the last line in the file it might not have one:
|
|
||||||
if (hit_eof_) {
|
|
||||||
assert(buf_used_);
|
|
||||||
// There's room for the NUL because of the buf_used_ == sizeof(buf_)
|
|
||||||
// check above.
|
|
||||||
buf_[buf_used_] = 0;
|
|
||||||
*len = buf_used_;
|
|
||||||
buf_used_ += 1; // since we appended the NUL.
|
|
||||||
*line = buf_;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we should pull in more data from the file
|
|
||||||
const ssize_t n = sys_read(fd_, buf_ + buf_used_,
|
|
||||||
sizeof(buf_) - buf_used_);
|
|
||||||
if (n < 0) {
|
|
||||||
return false;
|
|
||||||
} else if (n == 0) {
|
|
||||||
hit_eof_ = true;
|
|
||||||
} else {
|
|
||||||
buf_used_ += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, we have either set the hit_eof_ flag, or we have more
|
|
||||||
// data to process...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PopLine(unsigned len) {
|
|
||||||
// len doesn't include the NUL byte at the end.
|
|
||||||
|
|
||||||
assert(buf_used_ >= len + 1);
|
|
||||||
buf_used_ -= len + 1;
|
|
||||||
my_memmove(buf_, buf_ + len + 1, buf_used_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int fd_;
|
|
||||||
|
|
||||||
bool hit_eof_;
|
|
||||||
unsigned buf_used_;
|
|
||||||
char buf_[kMaxLineLen];
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
|
|
@ -1,169 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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 "client/linux/minidump_writer/line_reader.h"
|
|
||||||
#include "breakpad_googletest_includes.h"
|
|
||||||
#include "common/linux/tests/auto_testfile.h"
|
|
||||||
|
|
||||||
using namespace google_breakpad;
|
|
||||||
|
|
||||||
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 char *line;
|
|
||||||
unsigned len;
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LineReaderTest, OneLineTerminated) {
|
|
||||||
ScopedTestFile file("a\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
LineReader reader(file.GetFd());
|
|
||||||
|
|
||||||
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]);
|
|
||||||
reader.PopLine(len);
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LineReaderTest, OneLine) {
|
|
||||||
ScopedTestFile file("a");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
LineReader reader(file.GetFd());
|
|
||||||
|
|
||||||
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]);
|
|
||||||
reader.PopLine(len);
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LineReaderTest, TwoLinesTerminated) {
|
|
||||||
ScopedTestFile file("a\nb\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
LineReader reader(file.GetFd());
|
|
||||||
|
|
||||||
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]);
|
|
||||||
reader.PopLine(len);
|
|
||||||
|
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
|
||||||
ASSERT_EQ((unsigned)1, len);
|
|
||||||
ASSERT_EQ('b', line[0]);
|
|
||||||
ASSERT_EQ('\0', line[1]);
|
|
||||||
reader.PopLine(len);
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LineReaderTest, TwoLines) {
|
|
||||||
ScopedTestFile file("a\nb");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
LineReader reader(file.GetFd());
|
|
||||||
|
|
||||||
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]);
|
|
||||||
reader.PopLine(len);
|
|
||||||
|
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
|
||||||
ASSERT_EQ((unsigned)1, len);
|
|
||||||
ASSERT_EQ('b', line[0]);
|
|
||||||
ASSERT_EQ('\0', line[1]);
|
|
||||||
reader.PopLine(len);
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LineReaderTest, MaxLength) {
|
|
||||||
char l[LineReader::kMaxLineLen-1];
|
|
||||||
memset(l, 'a', sizeof(l));
|
|
||||||
ScopedTestFile file(l, sizeof(l));
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
LineReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char *line;
|
|
||||||
unsigned len;
|
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
|
||||||
ASSERT_EQ(sizeof(l), len);
|
|
||||||
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
|
|
||||||
ASSERT_EQ('\0', line[len]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LineReaderTest, TooLong) {
|
|
||||||
// Note: this writes kMaxLineLen 'a' chars in the test file.
|
|
||||||
char l[LineReader::kMaxLineLen];
|
|
||||||
memset(l, 'a', sizeof(l));
|
|
||||||
ScopedTestFile file(l, sizeof(l));
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
LineReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char *line;
|
|
||||||
unsigned len;
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len));
|
|
||||||
}
|
|
@ -1,250 +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>
|
|
||||||
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
|
|
||||||
const char* core_path,
|
|
||||||
const char* procfs_path)
|
|
||||||
: LinuxDumper(pid),
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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->regs.regs[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__)
|
|
||||||
for (int i = EF_REG0; i <= EF_REG31; i++)
|
|
||||||
info.regs.regs[i - EF_REG0] = status->pr_reg[i];
|
|
||||||
|
|
||||||
info.regs.lo = status->pr_reg[EF_LO];
|
|
||||||
info.regs.hi = status->pr_reg[EF_HI];
|
|
||||||
info.regs.epc = status->pr_reg[EF_CP0_EPC];
|
|
||||||
info.regs.badvaddr = status->pr_reg[EF_CP0_BADVADDR];
|
|
||||||
info.regs.status = status->pr_reg[EF_CP0_STATUS];
|
|
||||||
info.regs.cause = status->pr_reg[EF_CP0_CAUSE];
|
|
||||||
#else
|
|
||||||
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
|
|
||||||
#endif
|
|
||||||
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,122 +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
|
|
||||||
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path);
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
virtual void 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,118 +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, 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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,475 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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_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
|
|
||||||
// use the alternative allocator.
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <elf.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.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"
|
|
||||||
|
|
||||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
|
||||||
static const char kDeletedSuffix[] = " (deleted)";
|
|
||||||
static const char kReservedFlags[] = " ---p";
|
|
||||||
|
|
||||||
inline static bool IsMappedFileOpenUnsafe(
|
|
||||||
const google_breakpad::MappingInfo& mapping) {
|
|
||||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
|
||||||
// because the semantics of the open may be driver-specific so we'd risk
|
|
||||||
// hanging the crash dumper. And a file in /dev/ almost certainly has no
|
|
||||||
// ELF file identifier anyways.
|
|
||||||
return my_strncmp(mapping.name,
|
|
||||||
kMappedFileUnsafePrefix,
|
|
||||||
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
|
||||||
#define AT_MAX AT_SYSINFO_EHDR
|
|
||||||
|
|
||||||
LinuxDumper::LinuxDumper(pid_t pid)
|
|
||||||
: pid_(pid),
|
|
||||||
crash_address_(0),
|
|
||||||
crash_signal_(0),
|
|
||||||
crash_thread_(pid),
|
|
||||||
threads_(&allocator_, 8),
|
|
||||||
mappings_(&allocator_),
|
|
||||||
auxv_(&allocator_, AT_MAX + 1) {
|
|
||||||
// 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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LinuxDumper::Init() {
|
|
||||||
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|
||||||
bool member,
|
|
||||||
unsigned int mapping_id,
|
|
||||||
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) {
|
|
||||||
void* linux_gate = NULL;
|
|
||||||
if (pid_ == sys_getpid()) {
|
|
||||||
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
|
|
||||||
} else {
|
|
||||||
linux_gate = allocator_.Alloc(mapping.size);
|
|
||||||
CopyFromProcess(linux_gate, pid_,
|
|
||||||
reinterpret_cast<const void*>(mapping.start_addr),
|
|
||||||
mapping.size);
|
|
||||||
}
|
|
||||||
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
char filename[NAME_MAX];
|
|
||||||
size_t filename_len = my_strlen(mapping.name);
|
|
||||||
if (filename_len >= NAME_MAX) {
|
|
||||||
assert(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
my_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)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool success =
|
|
||||||
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
|
||||||
if (success && member && filename_modified) {
|
|
||||||
mappings_[mapping_id]->name[filename_len -
|
|
||||||
sizeof(kDeletedSuffix) + 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 MappingInfo& mapping, char* soname, size_t soname_size) {
|
|
||||||
if (IsMappedFileOpenUnsafe(mapping)) {
|
|
||||||
// Not safe
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char filename[NAME_MAX];
|
|
||||||
size_t filename_len = my_strlen(mapping.name);
|
|
||||||
if (filename_len >= NAME_MAX) {
|
|
||||||
assert(false);
|
|
||||||
// name too long
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
my_memcpy(filename, mapping.name, filename_len);
|
|
||||||
filename[filename_len] = '\0';
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
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(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() {
|
|
||||||
char auxv_path[NAME_MAX];
|
|
||||||
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = sys_open(auxv_path, O_RDONLY, 0);
|
|
||||||
if (fd < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sys_close(fd);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LinuxDumper::EnumerateMappings() {
|
|
||||||
char maps_path[NAME_MAX];
|
|
||||||
if (!BuildProcPath(maps_path, pid_, "maps"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 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]);
|
|
||||||
|
|
||||||
const int fd = sys_open(maps_path, O_RDONLY, 0);
|
|
||||||
if (fd < 0)
|
|
||||||
return false;
|
|
||||||
LineReader* const line_reader = new(allocator_) LineReader(fd);
|
|
||||||
|
|
||||||
const char* line;
|
|
||||||
unsigned line_len;
|
|
||||||
while (line_reader->GetNextLine(&line, &line_len)) {
|
|
||||||
uintptr_t start_addr, end_addr, offset;
|
|
||||||
|
|
||||||
const char* i1 = my_read_hex_ptr(&start_addr, line);
|
|
||||||
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;
|
|
||||||
// Only copy name if the name is a valid path name, or if
|
|
||||||
// it's the VDSO image.
|
|
||||||
if (((name = my_strchr(line, '/')) == NULL) &&
|
|
||||||
linux_gate_loc &&
|
|
||||||
reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
|
|
||||||
name = kLinuxGateLibraryName;
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
// 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 ((start_addr == module->start_addr + module->size) &&
|
|
||||||
(my_strlen(name) == my_strlen(module->name)) &&
|
|
||||||
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
|
|
||||||
module->size = end_addr - module->start_addr;
|
|
||||||
line_reader->PopLine(line_len);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Also merge mappings that result from address ranges that the
|
|
||||||
// linker reserved but which a loaded library did not use. These
|
|
||||||
// appear as an anonymous private mapping with no access flags set
|
|
||||||
// and which directly follow an executable mapping.
|
|
||||||
if (!name && !mappings_.empty()) {
|
|
||||||
MappingInfo* module = mappings_.back();
|
|
||||||
if ((start_addr == module->start_addr + module->size) &&
|
|
||||||
module->exec &&
|
|
||||||
module->name[0] == '/' &&
|
|
||||||
offset == 0 && my_strncmp(i2,
|
|
||||||
kReservedFlags,
|
|
||||||
sizeof(kReservedFlags) - 1) == 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));
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line_reader->PopLine(line_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_close(fd);
|
|
||||||
|
|
||||||
return !mappings_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// unwind. So we just grab, up to, 32k of stack.
|
|
||||||
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
|
||||||
uintptr_t int_stack_pointer) {
|
|
||||||
// Move the stack pointer to the bottom of the page that it's in.
|
|
||||||
const uintptr_t page_size = getpagesize();
|
|
||||||
|
|
||||||
uint8_t* const stack_pointer =
|
|
||||||
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
|
|
||||||
|
|
||||||
// The number of bytes of stack which we try to capture.
|
|
||||||
static const ptrdiff_t kStackToCapture = 32 * 1024;
|
|
||||||
|
|
||||||
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 distance_to_end =
|
|
||||||
static_cast<ptrdiff_t>(mapping->size) - offset;
|
|
||||||
*stack_len = distance_to_end > kStackToCapture ?
|
|
||||||
kStackToCapture : distance_to_end;
|
|
||||||
*stack = stack_pointer;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
|
||||||
const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
|
|
||||||
if (addr >= start && addr - start < mappings_[i]->size)
|
|
||||||
return mappings_[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
|
||||||
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
|
|
||||||
|
|
||||||
// Check for ' (deleted)' in |path|.
|
|
||||||
// |path| has to be at least as long as "/x (deleted)".
|
|
||||||
const size_t path_len = my_strlen(path);
|
|
||||||
if (path_len < kDeletedSuffixLen + 2)
|
|
||||||
return false;
|
|
||||||
if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
|
|
||||||
kDeletedSuffixLen) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check |path| against the /proc/pid/exe 'symlink'.
|
|
||||||
char exe_link[NAME_MAX];
|
|
||||||
char new_path[NAME_MAX];
|
|
||||||
if (!BuildProcPath(exe_link, pid_, "exe"))
|
|
||||||
return false;
|
|
||||||
if (!SafeReadLink(exe_link, new_path))
|
|
||||||
return false;
|
|
||||||
if (my_strcmp(path, new_path) != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check to see if someone actually named their executable 'foo (deleted)'.
|
|
||||||
struct kernel_stat exe_stat;
|
|
||||||
struct kernel_stat new_path_stat;
|
|
||||||
if (sys_stat(exe_link, &exe_stat) == 0 &&
|
|
||||||
sys_stat(new_path, &new_path_stat) == 0 &&
|
|
||||||
exe_stat.st_dev == new_path_stat.st_dev &&
|
|
||||||
exe_stat.st_ino == new_path_stat.st_ino) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
my_memcpy(path, exe_link, NAME_MAX);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,187 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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_dumper.h: Define the google_breakpad::LinuxDumper class, which
|
|
||||||
// is a base class for extracting information of a crashed process. It
|
|
||||||
// was originally a complete implementation using the ptrace API, but
|
|
||||||
// has been refactored to allow derived implementations supporting both
|
|
||||||
// ptrace and core dump. A portion of the original implementation is now
|
|
||||||
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
|
|
||||||
// details).
|
|
||||||
|
|
||||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
|
||||||
|
|
||||||
#include <elf.h>
|
|
||||||
#include <linux/limits.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/user.h>
|
|
||||||
|
|
||||||
#include "client/linux/dump_writer_common/mapping_info.h"
|
|
||||||
#include "client/linux/dump_writer_common/thread_info.h"
|
|
||||||
#include "common/memory.h"
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
|
||||||
#if defined(__i386) || defined(__ARM_EABI__) || defined(__mips__)
|
|
||||||
typedef Elf32_auxv_t elf_aux_entry;
|
|
||||||
#elif defined(__x86_64) || defined(__aarch64__)
|
|
||||||
typedef Elf64_auxv_t elf_aux_entry;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
|
||||||
|
|
||||||
// When we find the VDSO mapping in the process's address space, this
|
|
||||||
// is the name we use for it when writing it to the minidump.
|
|
||||||
// This should always be less than NAME_MAX!
|
|
||||||
const char kLinuxGateLibraryName[] = "linux-gate.so";
|
|
||||||
|
|
||||||
class LinuxDumper {
|
|
||||||
public:
|
|
||||||
explicit LinuxDumper(pid_t pid);
|
|
||||||
|
|
||||||
virtual ~LinuxDumper();
|
|
||||||
|
|
||||||
// Parse the data for |threads| and |mappings|.
|
|
||||||
virtual bool Init();
|
|
||||||
|
|
||||||
// Return true if the dumper performs a post-mortem dump.
|
|
||||||
virtual bool IsPostMortem() const = 0;
|
|
||||||
|
|
||||||
// Suspend/resume all threads in the given process.
|
|
||||||
virtual bool ThreadsSuspend() = 0;
|
|
||||||
virtual bool ThreadsResume() = 0;
|
|
||||||
|
|
||||||
// Read 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) = 0;
|
|
||||||
|
|
||||||
// These are only valid after a call to |Init|.
|
|
||||||
const wasteful_vector<pid_t> &threads() { return threads_; }
|
|
||||||
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
|
||||||
const MappingInfo* FindMapping(const void* address) const;
|
|
||||||
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
|
|
||||||
|
|
||||||
// Find a block of memory to take as the stack given the top of stack pointer.
|
|
||||||
// stack: (output) the lowest address in the memory area
|
|
||||||
// stack_len: (output) the length of the memory area
|
|
||||||
// stack_top: the current top of the stack
|
|
||||||
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
|
|
||||||
|
|
||||||
PageAllocator* allocator() { return &allocator_; }
|
|
||||||
|
|
||||||
// Copy content of |length| bytes from a given process |child|,
|
|
||||||
// starting from |src|, into |dest|.
|
|
||||||
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
|
|
||||||
size_t length) = 0;
|
|
||||||
|
|
||||||
// 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. Returns true on
|
|
||||||
// success.
|
|
||||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
|
|
||||||
|
|
||||||
// Generate a File ID from the .text section of a mapped entry.
|
|
||||||
// If not a member, mapping_id is ignored. This method can also manipulate the
|
|
||||||
// |mapping|.name to truncate "(deleted)" from the file name if necessary.
|
|
||||||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|
||||||
bool member,
|
|
||||||
unsigned int mapping_id,
|
|
||||||
uint8_t identifier[sizeof(MDGUID)]);
|
|
||||||
|
|
||||||
uintptr_t crash_address() const { return crash_address_; }
|
|
||||||
void set_crash_address(uintptr_t crash_address) {
|
|
||||||
crash_address_ = crash_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
int crash_signal() const { return crash_signal_; }
|
|
||||||
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
|
|
||||||
|
|
||||||
pid_t crash_thread() const { return crash_thread_; }
|
|
||||||
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
|
||||||
|
|
||||||
// Extracts the effective path and file name of from |mapping|. In most cases
|
|
||||||
// the effective name/path are just the mapping's path and basename. In some
|
|
||||||
// other cases, however, a library can be mapped from an archive (e.g., when
|
|
||||||
// loading .so libs from an apk on Android) and this method is able to
|
|
||||||
// reconstruct the original file name.
|
|
||||||
static void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
|
||||||
char* file_path,
|
|
||||||
size_t file_path_size,
|
|
||||||
char* file_name,
|
|
||||||
size_t file_name_size);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool ReadAuxv();
|
|
||||||
|
|
||||||
virtual bool EnumerateMappings();
|
|
||||||
|
|
||||||
virtual bool EnumerateThreads() = 0;
|
|
||||||
|
|
||||||
// For the case where a running program has been deleted, it'll show up in
|
|
||||||
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
|
|
||||||
// see if '/path/to/program (deleted)' matches /proc/pid/exe and return
|
|
||||||
// /proc/pid/exe in |path| so ELF identifier generation works correctly. This
|
|
||||||
// also checks to see if '/path/to/program (deleted)' exists, so it does not
|
|
||||||
// get fooled by a poorly named binary.
|
|
||||||
// For programs that don't end with ' (deleted)', this is a no-op.
|
|
||||||
// This assumes |path| is a buffer with length NAME_MAX.
|
|
||||||
// Returns true if |path| is modified.
|
|
||||||
bool HandleDeletedFileInMapping(char* path) const;
|
|
||||||
|
|
||||||
// ID of the crashed process.
|
|
||||||
const pid_t pid_;
|
|
||||||
|
|
||||||
// Virtual address at which the process crashed.
|
|
||||||
uintptr_t crash_address_;
|
|
||||||
|
|
||||||
// Signal that terminated the crashed process.
|
|
||||||
int crash_signal_;
|
|
||||||
|
|
||||||
// ID of the crashed thread.
|
|
||||||
pid_t crash_thread_;
|
|
||||||
|
|
||||||
mutable PageAllocator allocator_;
|
|
||||||
|
|
||||||
// IDs of all the threads.
|
|
||||||
wasteful_vector<pid_t> threads_;
|
|
||||||
|
|
||||||
// Info from /proc/<pid>/maps.
|
|
||||||
wasteful_vector<MappingInfo*> mappings_;
|
|
||||||
|
|
||||||
// Info from /proc/<pid>/auxv
|
|
||||||
wasteful_vector<elf_aux_val_t> auxv_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
|
|
@ -1,94 +0,0 @@
|
|||||||
// 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
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// Helper program for the linux_dumper class, which creates a bunch of
|
|
||||||
// threads. The first word of each thread's stack is set to the thread
|
|
||||||
// id.
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
|
|
||||||
#if defined(__ARM_EABI__)
|
|
||||||
#define TID_PTR_REGISTER "r3"
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#define TID_PTR_REGISTER "x3"
|
|
||||||
#elif defined(__i386)
|
|
||||||
#define TID_PTR_REGISTER "ecx"
|
|
||||||
#elif defined(__x86_64)
|
|
||||||
#define TID_PTR_REGISTER "rcx"
|
|
||||||
#elif defined(__mips__)
|
|
||||||
#define TID_PTR_REGISTER "$1"
|
|
||||||
#else
|
|
||||||
#error This test has not been ported to this platform.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void *thread_function(void *data) {
|
|
||||||
int pipefd = *static_cast<int *>(data);
|
|
||||||
volatile pid_t thread_id = syscall(__NR_gettid);
|
|
||||||
// Signal parent that a thread has started.
|
|
||||||
uint8_t byte = 1;
|
|
||||||
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
|
|
||||||
perror("ERROR: parent notification failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
|
|
||||||
while (true)
|
|
||||||
asm volatile ("" : : "r" (thread_id_ptr));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if (argc < 3) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int pipefd = atoi(argv[1]);
|
|
||||||
int num_threads = atoi(argv[2]);
|
|
||||||
if (num_threads < 1) {
|
|
||||||
fprintf(stderr, "ERROR: number of threads is 0");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]);
|
|
||||||
pthread_attr_t thread_attributes;
|
|
||||||
pthread_attr_init(&thread_attributes);
|
|
||||||
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
|
|
||||||
for (int i = 1; i < num_threads; i++) {
|
|
||||||
pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd);
|
|
||||||
}
|
|
||||||
thread_function(&pipefd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,338 +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_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
|
|
||||||
// See linux_ptrace_dumper.h for detals.
|
|
||||||
// This class was originally splitted from google_breakpad::LinuxDumper.
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// use the alternative allocator.
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
|
||||||
|
|
||||||
#include <asm/ptrace.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/ptrace.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
#if defined(__i386)
|
|
||||||
#include <cpuid.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/directory_reader.h"
|
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
|
|
||||||
// Suspends 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resumes a thread by detaching from it.
|
|
||||||
static bool ResumeThread(pid_t pid) {
|
|
||||||
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
|
|
||||||
: LinuxDumper(pid),
|
|
||||||
threads_suspended_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
|
|
||||||
const char* node) const {
|
|
||||||
if (!path || !node || pid <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
size_t node_len = my_strlen(node);
|
|
||||||
if (node_len == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const unsigned pid_len = my_uint_len(pid);
|
|
||||||
const size_t total_length = 6 + pid_len + 1 + node_len;
|
|
||||||
if (total_length >= NAME_MAX)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
my_memcpy(path, "/proc/", 6);
|
|
||||||
my_uitos(path + 6, pid, pid_len);
|
|
||||||
path[6 + pid_len] = '/';
|
|
||||||
my_memcpy(path + 6 + pid_len + 1, node, node_len);
|
|
||||||
path[total_length] = '\0';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LinuxPtraceDumper::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;
|
|
||||||
}
|
|
||||||
my_memcpy(local + done, &tmp, l);
|
|
||||||
done += l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
|
||||||
if (index >= threads_.size())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pid_t tid = threads_[index];
|
|
||||||
|
|
||||||
assert(info != NULL);
|
|
||||||
char status_path[NAME_MAX];
|
|
||||||
if (!BuildProcPath(status_path, tid, "status"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const int fd = sys_open(status_path, O_RDONLY, 0);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
line_reader->PopLine(line_len);
|
|
||||||
}
|
|
||||||
sys_close(fd);
|
|
||||||
|
|
||||||
if (info->ppid == -1 || info->tgid == -1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef PTRACE_GETREGSET
|
|
||||||
struct iovec io;
|
|
||||||
io.iov_base = &info->regs;
|
|
||||||
io.iov_len = sizeof(info->regs);
|
|
||||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
io.iov_base = &info->fpregs;
|
|
||||||
io.iov_len = sizeof(info->fpregs);
|
|
||||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__i386)
|
|
||||||
#if !defined(bit_FXSAVE) // e.g. Clang
|
|
||||||
#define bit_FXSAVE bit_FXSR
|
|
||||||
#endif
|
|
||||||
// Detect if the CPU supports the FXSAVE/FXRSTOR instructions
|
|
||||||
int eax, ebx, ecx, edx;
|
|
||||||
__cpuid(1, eax, ebx, ecx, edx);
|
|
||||||
if (edx & bit_FXSAVE) {
|
|
||||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
memset(&info->fpxregs, 0, sizeof(info->fpxregs));
|
|
||||||
}
|
|
||||||
#endif // defined(__i386)
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__mips__)
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
|
||||||
reinterpret_cast<void*>(DSP_BASE + (i * 2)), &info->hi[i]);
|
|
||||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
|
||||||
reinterpret_cast<void*>(DSP_BASE + (i * 2) + 1), &info->lo[i]);
|
|
||||||
}
|
|
||||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
|
||||||
reinterpret_cast<void*>(DSP_CONTROL), &info->dsp_control);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uint8_t* stack_pointer;
|
|
||||||
#if defined(__i386)
|
|
||||||
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
|
||||||
#elif defined(__x86_64)
|
|
||||||
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
|
||||||
#elif defined(__ARM_EABI__)
|
|
||||||
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
|
||||||
#elif defined(__mips__)
|
|
||||||
stack_pointer =
|
|
||||||
reinterpret_cast<uint8_t*>(info->regs.regs[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 LinuxPtraceDumper::IsPostMortem() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LinuxPtraceDumper::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.
|
|
||||||
my_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 LinuxPtraceDumper::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse /proc/$pid/task to list all the threads of the process identified by
|
|
||||||
// pid.
|
|
||||||
bool LinuxPtraceDumper::EnumerateThreads() {
|
|
||||||
char task_path[NAME_MAX];
|
|
||||||
if (!BuildProcPath(task_path, pid_, "task"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
|
|
||||||
if (fd < 0)
|
|
||||||
return false;
|
|
||||||
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
threads_.push_back(tid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dir_reader->PopEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_close(fd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,92 +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_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
|
|
||||||
// class, which is derived from google_breakpad::LinuxDumper to extract
|
|
||||||
// information from a crashed process via ptrace.
|
|
||||||
// This class was originally splitted from google_breakpad::LinuxDumper.
|
|
||||||
|
|
||||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
class LinuxPtraceDumper : public LinuxDumper {
|
|
||||||
public:
|
|
||||||
// Constructs a dumper for extracting information of a given process
|
|
||||||
// with a process ID of |pid|.
|
|
||||||
explicit LinuxPtraceDumper(pid_t pid);
|
|
||||||
|
|
||||||
// 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. Returns true on
|
|
||||||
// success.
|
|
||||||
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 uses ptrace to extract
|
|
||||||
// the content from the target process.
|
|
||||||
virtual void 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 false to indicate this dumper performs a dump of
|
|
||||||
// a crashed process via ptrace.
|
|
||||||
virtual bool IsPostMortem() const;
|
|
||||||
|
|
||||||
// Implements LinuxDumper::ThreadsSuspend().
|
|
||||||
// Suspends all threads in the given process. Returns true on success.
|
|
||||||
virtual bool ThreadsSuspend();
|
|
||||||
|
|
||||||
// Implements LinuxDumper::ThreadsResume().
|
|
||||||
// Resumes all threads in the given process. Returns true on success.
|
|
||||||
virtual bool ThreadsResume();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Implements LinuxDumper::EnumerateThreads().
|
|
||||||
// Enumerates all threads of the given process into |threads_|.
|
|
||||||
virtual bool EnumerateThreads();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Set to true if all threads of the crashed process are suspended.
|
|
||||||
bool threads_suspended_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_
|
|
@ -1,463 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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_ptrace_dumper_unittest.cc:
|
|
||||||
// Unit tests for google_breakpad::LinuxPtraceDumper.
|
|
||||||
//
|
|
||||||
// This file was renamed from linux_dumper_unittest.cc and modified due
|
|
||||||
// to LinuxDumper being splitted into two classes.
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
|
||||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
|
||||||
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
|
|
||||||
#include "common/linux/eintr_wrapper.h"
|
|
||||||
#include "common/linux/file_id.h"
|
|
||||||
#include "common/linux/ignore_ret.h"
|
|
||||||
#include "common/linux/safe_readlink.h"
|
|
||||||
#include "common/memory.h"
|
|
||||||
#include "common/using_std_string.h"
|
|
||||||
|
|
||||||
#ifndef PR_SET_PTRACER
|
|
||||||
#define PR_SET_PTRACER 0x59616d61
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace google_breakpad;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
typedef testing::Test LinuxPtraceDumperTest;
|
|
||||||
|
|
||||||
/* Fixture for running tests in a child process. */
|
|
||||||
class LinuxPtraceDumperChildTest : public testing::Test {
|
|
||||||
protected:
|
|
||||||
virtual void SetUp() {
|
|
||||||
child_pid_ = fork();
|
|
||||||
#ifndef __ANDROID__
|
|
||||||
prctl(PR_SET_PTRACER, child_pid_);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gtest is calling TestBody from this class, which sets up a child
|
|
||||||
* process in which the RealTestBody virtual member is called.
|
|
||||||
* As such, TestBody is not supposed to be overridden in derived classes.
|
|
||||||
*/
|
|
||||||
virtual void TestBody() /* final */ {
|
|
||||||
if (child_pid_ == 0) {
|
|
||||||
// child process
|
|
||||||
RealTestBody();
|
|
||||||
exit(HasFatalFailure() ? kFatalFailure :
|
|
||||||
(HasNonfatalFailure() ? kNonFatalFailure : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_TRUE(child_pid_ > 0);
|
|
||||||
int status;
|
|
||||||
waitpid(child_pid_, &status, 0);
|
|
||||||
if (WEXITSTATUS(status) == kFatalFailure) {
|
|
||||||
GTEST_FATAL_FAILURE_("Test failed in child process");
|
|
||||||
} else if (WEXITSTATUS(status) == kNonFatalFailure) {
|
|
||||||
GTEST_NONFATAL_FAILURE_("Test failed in child process");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gtest defines TestBody functions through its macros, but classes
|
|
||||||
* derived from this one need to define RealTestBody instead.
|
|
||||||
* This is achieved by defining a TestBody macro further below.
|
|
||||||
*/
|
|
||||||
virtual void RealTestBody() = 0;
|
|
||||||
private:
|
|
||||||
static const int kFatalFailure = 1;
|
|
||||||
static const int kNonFatalFailure = 2;
|
|
||||||
|
|
||||||
pid_t child_pid_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/* Replace TestBody declarations within TEST*() with RealTestBody
|
|
||||||
* declarations */
|
|
||||||
#define TestBody RealTestBody
|
|
||||||
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, Setup) {
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
|
|
||||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
|
|
||||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
|
|
||||||
ASSERT_FALSE(dumper.FindMapping(NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
|
|
||||||
ASSERT_GE(dumper.threads().size(), (size_t)1);
|
|
||||||
bool found = false;
|
|
||||||
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
|
||||||
if (dumper.threads()[i] == getppid()) {
|
|
||||||
ASSERT_FALSE(found);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(found);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper stack class to close a file descriptor and unmap
|
|
||||||
// a mmap'ed mapping.
|
|
||||||
class StackHelper {
|
|
||||||
public:
|
|
||||||
StackHelper()
|
|
||||||
: fd_(-1), mapping_(NULL), size_(0) {}
|
|
||||||
~StackHelper() {
|
|
||||||
if (size_)
|
|
||||||
munmap(mapping_, size_);
|
|
||||||
if (fd_ >= 0)
|
|
||||||
close(fd_);
|
|
||||||
}
|
|
||||||
void Init(int fd, char* mapping, size_t size) {
|
|
||||||
fd_ = fd;
|
|
||||||
mapping_ = mapping;
|
|
||||||
size_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* mapping() const { return mapping_; }
|
|
||||||
size_t size() const { return size_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int fd_;
|
|
||||||
char* mapping_;
|
|
||||||
size_t size_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
|
|
||||||
protected:
|
|
||||||
virtual void SetUp();
|
|
||||||
|
|
||||||
string helper_path_;
|
|
||||||
size_t page_size_;
|
|
||||||
StackHelper helper_;
|
|
||||||
};
|
|
||||||
|
|
||||||
void LinuxPtraceDumperMappingsTest::SetUp() {
|
|
||||||
helper_path_ = GetHelperBinary();
|
|
||||||
if (helper_path_.empty()) {
|
|
||||||
FAIL() << "Couldn't find helper binary";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mmap two segments out of the helper binary, one
|
|
||||||
// enclosed in the other, but with different protections.
|
|
||||||
page_size_ = sysconf(_SC_PAGESIZE);
|
|
||||||
const size_t kMappingSize = 3 * page_size_;
|
|
||||||
int fd = open(helper_path_.c_str(), O_RDONLY);
|
|
||||||
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
|
|
||||||
<< ", Error: " << strerror(errno);
|
|
||||||
char* mapping =
|
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
|
||||||
kMappingSize,
|
|
||||||
PROT_READ,
|
|
||||||
MAP_SHARED,
|
|
||||||
fd,
|
|
||||||
0));
|
|
||||||
ASSERT_TRUE(mapping);
|
|
||||||
|
|
||||||
// Ensure that things get cleaned up.
|
|
||||||
helper_.Init(fd, mapping, kMappingSize);
|
|
||||||
|
|
||||||
// Carve a page out of the first mapping with different permissions.
|
|
||||||
char* inside_mapping = reinterpret_cast<char*>(
|
|
||||||
mmap(mapping + 2 * page_size_,
|
|
||||||
page_size_,
|
|
||||||
PROT_NONE,
|
|
||||||
MAP_SHARED | MAP_FIXED,
|
|
||||||
fd,
|
|
||||||
// Map a different offset just to
|
|
||||||
// better test real-world conditions.
|
|
||||||
page_size_));
|
|
||||||
ASSERT_TRUE(inside_mapping);
|
|
||||||
|
|
||||||
LinuxPtraceDumperChildTest::SetUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
|
|
||||||
// Now check that LinuxPtraceDumper interpreted the mappings properly.
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
int mapping_count = 0;
|
|
||||||
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
|
|
||||||
const MappingInfo& mapping = *dumper.mappings()[i];
|
|
||||||
if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
|
|
||||||
// This mapping should encompass the entire original mapped
|
|
||||||
// range.
|
|
||||||
EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
|
|
||||||
mapping.start_addr);
|
|
||||||
EXPECT_EQ(this->helper_.size(), mapping.size);
|
|
||||||
EXPECT_EQ(0U, mapping.offset);
|
|
||||||
mapping_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXPECT_EQ(1, mapping_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
|
|
||||||
const pid_t pid = getppid();
|
|
||||||
LinuxPtraceDumper dumper(pid);
|
|
||||||
|
|
||||||
char maps_path[NAME_MAX] = "";
|
|
||||||
char maps_path_expected[NAME_MAX];
|
|
||||||
snprintf(maps_path_expected, sizeof(maps_path_expected),
|
|
||||||
"/proc/%d/maps", pid);
|
|
||||||
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, 0, "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("/proc/123") - 1;
|
|
||||||
memset(long_node, 'a', long_node_len);
|
|
||||||
long_node[long_node_len] = '\0';
|
|
||||||
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
|
||||||
// Ensure that the linux-gate VDSO is included in the mapping list.
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
|
|
||||||
void* linux_gate_loc =
|
|
||||||
reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
|
|
||||||
ASSERT_TRUE(linux_gate_loc);
|
|
||||||
bool found_linux_gate = false;
|
|
||||||
|
|
||||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
|
||||||
const MappingInfo* mapping;
|
|
||||||
for (unsigned i = 0; i < mappings.size(); ++i) {
|
|
||||||
mapping = mappings[i];
|
|
||||||
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
|
|
||||||
found_linux_gate = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXPECT_TRUE(found_linux_gate);
|
|
||||||
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
|
|
||||||
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
|
|
||||||
bool found_linux_gate = false;
|
|
||||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
|
||||||
unsigned index = 0;
|
|
||||||
for (unsigned i = 0; i < mappings.size(); ++i) {
|
|
||||||
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
|
|
||||||
found_linux_gate = true;
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(found_linux_gate);
|
|
||||||
|
|
||||||
// Need to suspend the child so ptrace actually works.
|
|
||||||
ASSERT_TRUE(dumper.ThreadsSuspend());
|
|
||||||
uint8_t identifier[sizeof(MDGUID)];
|
|
||||||
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
|
|
||||||
true,
|
|
||||||
index,
|
|
||||||
identifier));
|
|
||||||
uint8_t empty_identifier[sizeof(MDGUID)];
|
|
||||||
memset(empty_identifier, 0, sizeof(empty_identifier));
|
|
||||||
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
|
|
||||||
EXPECT_TRUE(dumper.ThreadsResume());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
|
|
||||||
// Calculate the File ID of our binary using both
|
|
||||||
// FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
|
|
||||||
// and ensure that we get the same result from both.
|
|
||||||
char exe_name[PATH_MAX];
|
|
||||||
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
|
|
||||||
|
|
||||||
LinuxPtraceDumper dumper(getppid());
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
|
||||||
bool found_exe = false;
|
|
||||||
unsigned i;
|
|
||||||
for (i = 0; i < mappings.size(); ++i) {
|
|
||||||
const MappingInfo* mapping = mappings[i];
|
|
||||||
if (!strcmp(mapping->name, exe_name)) {
|
|
||||||
found_exe = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(found_exe);
|
|
||||||
|
|
||||||
uint8_t identifier1[sizeof(MDGUID)];
|
|
||||||
uint8_t identifier2[sizeof(MDGUID)];
|
|
||||||
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
|
|
||||||
identifier1));
|
|
||||||
FileID fileid(exe_name);
|
|
||||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
|
|
||||||
char identifier_string1[37];
|
|
||||||
char identifier_string2[37];
|
|
||||||
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
|
|
||||||
37);
|
|
||||||
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
|
|
||||||
37);
|
|
||||||
EXPECT_STREQ(identifier_string1, identifier_string2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get back to normal behavior of TEST*() macros wrt TestBody. */
|
|
||||||
#undef TestBody
|
|
||||||
|
|
||||||
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
|
||||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
|
||||||
char kNumberOfThreadsArgument[2];
|
|
||||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
|
|
||||||
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
pid_t child_pid = fork();
|
|
||||||
if (child_pid == 0) {
|
|
||||||
// In child process.
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
string helper_path(GetHelperBinary());
|
|
||||||
if (helper_path.empty()) {
|
|
||||||
FAIL() << "Couldn't find helper binary";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass the pipe fd and the number of threads as arguments.
|
|
||||||
char pipe_fd_string[8];
|
|
||||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
|
||||||
execl(helper_path.c_str(),
|
|
||||||
"linux_dumper_unittest_helper",
|
|
||||||
pipe_fd_string,
|
|
||||||
kNumberOfThreadsArgument,
|
|
||||||
NULL);
|
|
||||||
// Kill if we get here.
|
|
||||||
printf("Errno from exec: %d", errno);
|
|
||||||
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
close(fds[1]);
|
|
||||||
|
|
||||||
// Wait for all child threads to indicate that they have started
|
|
||||||
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
|
|
||||||
struct pollfd pfd;
|
|
||||||
memset(&pfd, 0, sizeof(pfd));
|
|
||||||
pfd.fd = fds[0];
|
|
||||||
pfd.events = POLLIN | POLLERR;
|
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
|
||||||
ASSERT_EQ(1, r);
|
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN);
|
|
||||||
uint8_t junk;
|
|
||||||
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
|
|
||||||
static_cast<ssize_t>(sizeof(junk)));
|
|
||||||
}
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
// There is a race here because we may stop a child thread before
|
|
||||||
// it is actually running the busy loop. Empirically this sleep
|
|
||||||
// is sufficient to avoid the race.
|
|
||||||
usleep(100000);
|
|
||||||
|
|
||||||
// Children are ready now.
|
|
||||||
LinuxPtraceDumper dumper(child_pid);
|
|
||||||
ASSERT_TRUE(dumper.Init());
|
|
||||||
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
|
|
||||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
|
||||||
|
|
||||||
ThreadInfo one_thread;
|
|
||||||
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
|
||||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
|
|
||||||
const void* stack;
|
|
||||||
size_t stack_len;
|
|
||||||
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
|
|
||||||
one_thread.stack_pointer));
|
|
||||||
// In the helper program, we stored a pointer to the thread id in a
|
|
||||||
// specific register. Check that we can recover its value.
|
|
||||||
#if defined(__ARM_EABI__)
|
|
||||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
|
|
||||||
#elif defined(__i386)
|
|
||||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
|
|
||||||
#elif defined(__x86_64)
|
|
||||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
|
|
||||||
#elif defined(__mips__)
|
|
||||||
pid_t* process_tid_location =
|
|
||||||
reinterpret_cast<pid_t*>(one_thread.regs.regs[1]);
|
|
||||||
#else
|
|
||||||
#error This test has not been ported to this platform.
|
|
||||||
#endif
|
|
||||||
pid_t one_thread_id;
|
|
||||||
dumper.CopyFromProcess(&one_thread_id,
|
|
||||||
dumper.threads()[i],
|
|
||||||
process_tid_location,
|
|
||||||
4);
|
|
||||||
EXPECT_EQ(dumper.threads()[i], one_thread_id);
|
|
||||||
}
|
|
||||||
EXPECT_TRUE(dumper.ThreadsResume());
|
|
||||||
kill(child_pid, SIGKILL);
|
|
||||||
|
|
||||||
// Reap child
|
|
||||||
int status;
|
|
||||||
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
|
|
||||||
ASSERT_TRUE(WIFSIGNALED(status));
|
|
||||||
ASSERT_EQ(SIGKILL, WTERMSIG(status));
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,124 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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_MINIDUMP_WRITER_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ucontext.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
class ExceptionHandler;
|
|
||||||
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
typedef struct fpsimd_context fpstate_t;
|
|
||||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
|
||||||
typedef struct _libc_fpstate fpstate_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// These entries store a list of memory regions that the client wants included
|
|
||||||
// in the minidump.
|
|
||||||
struct AppMemory {
|
|
||||||
void* ptr;
|
|
||||||
size_t length;
|
|
||||||
|
|
||||||
bool operator==(const struct AppMemory& other) const {
|
|
||||||
return ptr == other.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const void* other) const {
|
|
||||||
return ptr == other;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
typedef std::list<AppMemory> AppMemoryList;
|
|
||||||
|
|
||||||
// Writes a minidump to the filesystem. 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.
|
|
||||||
// minidump_path: the path to the file to write to. This is opened O_EXCL and
|
|
||||||
// fails open fails.
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
// Returns true iff successful.
|
|
||||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
|
||||||
const void* blob, size_t blob_size);
|
|
||||||
// Same as above but takes an open file descriptor instead of a path.
|
|
||||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
|
||||||
const void* blob, size_t blob_size);
|
|
||||||
|
|
||||||
// Alternate form of WriteMinidump() that works with processes that
|
|
||||||
// are not expected to have crashed. If |process_blamed_thread| is
|
|
||||||
// meaningful, it will be the one from which a crash signature is
|
|
||||||
// extracted. It is not expected that this function will be called
|
|
||||||
// from a compromised context, but it is safe to do so.
|
|
||||||
bool WriteMinidump(const char* minidump_path, pid_t process,
|
|
||||||
pid_t process_blamed_thread);
|
|
||||||
|
|
||||||
// These overloads also allow passing a list of known mappings and
|
|
||||||
// a list of additional memory regions to be included in the minidump.
|
|
||||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
|
||||||
const void* blob, size_t blob_size,
|
|
||||||
const MappingList& mappings,
|
|
||||||
const AppMemoryList& appdata);
|
|
||||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
|
||||||
const void* blob, size_t blob_size,
|
|
||||||
const MappingList& mappings,
|
|
||||||
const AppMemoryList& appdata);
|
|
||||||
|
|
||||||
// These overloads also allow passing a file size limit for the minidump.
|
|
||||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
|
||||||
pid_t crashing_process,
|
|
||||||
const void* blob, size_t blob_size,
|
|
||||||
const MappingList& mappings,
|
|
||||||
const AppMemoryList& appdata);
|
|
||||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
|
||||||
pid_t crashing_process,
|
|
||||||
const void* blob, size_t blob_size,
|
|
||||||
const MappingList& mappings,
|
|
||||||
const AppMemoryList& appdata);
|
|
||||||
|
|
||||||
bool WriteMinidump(const char* filename,
|
|
||||||
const MappingList& mappings,
|
|
||||||
const AppMemoryList& appdata,
|
|
||||||
LinuxDumper* dumper);
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
|
@ -1,756 +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.
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <ucontext.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
|
||||||
#include "client/linux/handler/exception_handler.h"
|
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
|
||||||
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
|
|
||||||
#include "common/linux/eintr_wrapper.h"
|
|
||||||
#include "common/linux/file_id.h"
|
|
||||||
#include "common/linux/ignore_ret.h"
|
|
||||||
#include "common/linux/safe_readlink.h"
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
#include "common/tests/auto_tempdir.h"
|
|
||||||
#include "common/tests/file_utils.h"
|
|
||||||
#include "common/using_std_string.h"
|
|
||||||
#include "google_breakpad/processor/minidump.h"
|
|
||||||
|
|
||||||
using namespace google_breakpad;
|
|
||||||
|
|
||||||
// Length of a formatted GUID string =
|
|
||||||
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
|
|
||||||
const int kGUIDStringSize = 37;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
typedef testing::Test MinidumpWriterTest;
|
|
||||||
|
|
||||||
const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
|
|
||||||
|
|
||||||
TEST(MinidumpWriterTest, SetupWithPath) {
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
|
||||||
context.tid = child;
|
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
|
|
||||||
struct stat st;
|
|
||||||
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
|
||||||
ASSERT_GT(st.st_size, 0);
|
|
||||||
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MinidumpWriterTest, SetupWithFD) {
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
const pid_t child = fork();
|
|
||||||
if (child == 0) {
|
|
||||||
close(fds[1]);
|
|
||||||
char b;
|
|
||||||
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));
|
|
||||||
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
|
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
|
||||||
context.tid = child;
|
|
||||||
ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
|
|
||||||
struct stat st;
|
|
||||||
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
|
||||||
ASSERT_GT(st.st_size, 0);
|
|
||||||
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that mapping info can be specified when writing a minidump,
|
|
||||||
// and that it ends up in the module list of the minidump.
|
|
||||||
TEST(MinidumpWriterTest, MappingInfo) {
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
|
|
||||||
const char* kMemoryName = "a fake module";
|
|
||||||
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
|
|
||||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
||||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
|
||||||
};
|
|
||||||
char module_identifier_buffer[kGUIDStringSize];
|
|
||||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
|
||||||
module_identifier_buffer,
|
|
||||||
sizeof(module_identifier_buffer));
|
|
||||||
string module_identifier(module_identifier_buffer);
|
|
||||||
// Strip out dashes
|
|
||||||
size_t pos;
|
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
|
||||||
module_identifier.erase(pos, 1);
|
|
||||||
}
|
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0";
|
|
||||||
|
|
||||||
// Get some memory.
|
|
||||||
char* memory =
|
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
|
||||||
memory_size,
|
|
||||||
PROT_READ | PROT_WRITE,
|
|
||||||
MAP_PRIVATE | MAP_ANON,
|
|
||||||
-1,
|
|
||||||
0));
|
|
||||||
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
|
|
||||||
ASSERT_TRUE(memory);
|
|
||||||
|
|
||||||
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));
|
|
||||||
ASSERT_EQ(0, getcontext(&context.context));
|
|
||||||
context.tid = child;
|
|
||||||
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
|
|
||||||
// Add information about the mapped memory.
|
|
||||||
MappingInfo info;
|
|
||||||
info.start_addr = kMemoryAddress;
|
|
||||||
info.size = memory_size;
|
|
||||||
info.offset = 0;
|
|
||||||
strcpy(info.name, kMemoryName);
|
|
||||||
|
|
||||||
MappingList mappings;
|
|
||||||
AppMemoryList memory_list;
|
|
||||||
MappingEntry mapping;
|
|
||||||
mapping.first = info;
|
|
||||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
|
||||||
mappings.push_back(mapping);
|
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
|
||||||
mappings, memory_list));
|
|
||||||
|
|
||||||
// Read the minidump. Load the module list, and ensure that
|
|
||||||
// the mmap'ed |memory| is listed with the given module name
|
|
||||||
// and debug ID.
|
|
||||||
Minidump minidump(templ);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList();
|
|
||||||
ASSERT_TRUE(module_list);
|
|
||||||
const MinidumpModule* module =
|
|
||||||
module_list->GetModuleForAddress(kMemoryAddress);
|
|
||||||
ASSERT_TRUE(module);
|
|
||||||
|
|
||||||
EXPECT_EQ(kMemoryAddress, module->base_address());
|
|
||||||
EXPECT_EQ(memory_size, module->size());
|
|
||||||
EXPECT_EQ(kMemoryName, module->code_file());
|
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
|
||||||
|
|
||||||
uint32_t len;
|
|
||||||
// These streams are expected to be there
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
|
|
||||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
|
|
||||||
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that mapping info can be specified, and that it overrides
|
|
||||||
// existing mappings that are wholly contained within the specified
|
|
||||||
// range.
|
|
||||||
TEST(MinidumpWriterTest, MappingInfoContained) {
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const int32_t memory_size = sysconf(_SC_PAGESIZE);
|
|
||||||
const char* kMemoryName = "a fake module";
|
|
||||||
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
|
|
||||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
||||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
|
||||||
};
|
|
||||||
char module_identifier_buffer[kGUIDStringSize];
|
|
||||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
|
||||||
module_identifier_buffer,
|
|
||||||
sizeof(module_identifier_buffer));
|
|
||||||
string module_identifier(module_identifier_buffer);
|
|
||||||
// Strip out dashes
|
|
||||||
size_t pos;
|
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
|
||||||
module_identifier.erase(pos, 1);
|
|
||||||
}
|
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0";
|
|
||||||
|
|
||||||
// mmap a file
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
|
|
||||||
int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
|
|
||||||
ASSERT_NE(-1, fd);
|
|
||||||
unlink(tempfile.c_str());
|
|
||||||
// fill with zeros
|
|
||||||
google_breakpad::scoped_array<char> buffer(new char[memory_size]);
|
|
||||||
memset(buffer.get(), 0, memory_size);
|
|
||||||
ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
|
|
||||||
lseek(fd, 0, SEEK_SET);
|
|
||||||
|
|
||||||
char* memory =
|
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
|
||||||
memory_size,
|
|
||||||
PROT_READ | PROT_WRITE,
|
|
||||||
MAP_PRIVATE,
|
|
||||||
fd,
|
|
||||||
0));
|
|
||||||
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
|
|
||||||
ASSERT_TRUE(memory);
|
|
||||||
close(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));
|
|
||||||
context.tid = 1;
|
|
||||||
|
|
||||||
string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
|
|
||||||
// Add information about the mapped memory. Report it as being larger than
|
|
||||||
// it actually is.
|
|
||||||
MappingInfo info;
|
|
||||||
info.start_addr = kMemoryAddress - memory_size;
|
|
||||||
info.size = memory_size * 3;
|
|
||||||
info.offset = 0;
|
|
||||||
strcpy(info.name, kMemoryName);
|
|
||||||
|
|
||||||
MappingList mappings;
|
|
||||||
AppMemoryList memory_list;
|
|
||||||
MappingEntry mapping;
|
|
||||||
mapping.first = info;
|
|
||||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
|
||||||
mappings.push_back(mapping);
|
|
||||||
ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
|
|
||||||
mappings, memory_list));
|
|
||||||
|
|
||||||
// Read the minidump. Load the module list, and ensure that
|
|
||||||
// the mmap'ed |memory| is listed with the given module name
|
|
||||||
// and debug ID.
|
|
||||||
Minidump minidump(dumpfile);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList();
|
|
||||||
ASSERT_TRUE(module_list);
|
|
||||||
const MinidumpModule* module =
|
|
||||||
module_list->GetModuleForAddress(kMemoryAddress);
|
|
||||||
ASSERT_TRUE(module);
|
|
||||||
|
|
||||||
EXPECT_EQ(info.start_addr, module->base_address());
|
|
||||||
EXPECT_EQ(info.size, module->size());
|
|
||||||
EXPECT_EQ(kMemoryName, module->code_file());
|
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
|
||||||
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MinidumpWriterTest, DeletedBinary) {
|
|
||||||
const string kNumberOfThreadsArgument = "1";
|
|
||||||
const string helper_path(GetHelperBinary());
|
|
||||||
if (helper_path.empty()) {
|
|
||||||
FAIL() << "Couldn't find helper binary";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy binary to a temp file.
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
|
|
||||||
ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
|
|
||||||
<< "Failed to copy " << helper_path << " to " << binpath;
|
|
||||||
ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
|
|
||||||
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
pid_t child_pid = fork();
|
|
||||||
if (child_pid == 0) {
|
|
||||||
// In child process.
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
// Pass the pipe fd and the number of threads as arguments.
|
|
||||||
char pipe_fd_string[8];
|
|
||||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
|
||||||
execl(binpath.c_str(),
|
|
||||||
binpath.c_str(),
|
|
||||||
pipe_fd_string,
|
|
||||||
kNumberOfThreadsArgument.c_str(),
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
close(fds[1]);
|
|
||||||
// Wait for the child process to signal that it's ready.
|
|
||||||
struct pollfd pfd;
|
|
||||||
memset(&pfd, 0, sizeof(pfd));
|
|
||||||
pfd.fd = fds[0];
|
|
||||||
pfd.events = POLLIN | POLLERR;
|
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
|
||||||
ASSERT_EQ(1, r);
|
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN);
|
|
||||||
uint8_t junk;
|
|
||||||
const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
|
|
||||||
ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
// Child is ready now.
|
|
||||||
// Unlink the test binary.
|
|
||||||
unlink(binpath.c_str());
|
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context;
|
|
||||||
memset(&context, 0, sizeof(context));
|
|
||||||
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
|
||||||
context.tid = child_pid;
|
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
|
|
||||||
sizeof(context)));
|
|
||||||
kill(child_pid, SIGKILL);
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
|
||||||
ASSERT_GT(st.st_size, 0);
|
|
||||||
|
|
||||||
Minidump minidump(templ);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
|
|
||||||
// Check that the main module filename is correct.
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList();
|
|
||||||
ASSERT_TRUE(module_list);
|
|
||||||
const MinidumpModule* module = module_list->GetMainModule();
|
|
||||||
EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
|
|
||||||
// Check that the file ID is correct.
|
|
||||||
FileID fileid(helper_path.c_str());
|
|
||||||
uint8_t identifier[sizeof(MDGUID)];
|
|
||||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
|
|
||||||
char identifier_string[kGUIDStringSize];
|
|
||||||
FileID::ConvertIdentifierToString(identifier,
|
|
||||||
identifier_string,
|
|
||||||
kGUIDStringSize);
|
|
||||||
string module_identifier(identifier_string);
|
|
||||||
// Strip out dashes
|
|
||||||
size_t pos;
|
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
|
||||||
module_identifier.erase(pos, 1);
|
|
||||||
}
|
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0";
|
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that an additional memory region can be added to the minidump.
|
|
||||||
TEST(MinidumpWriterTest, AdditionalMemory) {
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
|
|
||||||
|
|
||||||
// Get some heap memory.
|
|
||||||
uint8_t* memory = new uint8_t[kMemorySize];
|
|
||||||
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
|
|
||||||
ASSERT_TRUE(memory);
|
|
||||||
|
|
||||||
// Stick some data into the memory so the contents can be verified.
|
|
||||||
for (uint32_t i = 0; i < kMemorySize; ++i) {
|
|
||||||
memory[i] = i % 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pid_t child = fork();
|
|
||||||
if (child == 0) {
|
|
||||||
close(fds[1]);
|
|
||||||
char b;
|
|
||||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
|
|
||||||
close(fds[0]);
|
|
||||||
syscall(__NR_exit);
|
|
||||||
}
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context;
|
|
||||||
|
|
||||||
// This needs a valid context for minidump writing to work, but getting
|
|
||||||
// a useful one from the child is too much work, so just use one from
|
|
||||||
// the parent since the child is just a forked copy anyway.
|
|
||||||
ASSERT_EQ(0, getcontext(&context.context));
|
|
||||||
context.tid = child;
|
|
||||||
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
unlink(templ.c_str());
|
|
||||||
|
|
||||||
MappingList mappings;
|
|
||||||
AppMemoryList memory_list;
|
|
||||||
|
|
||||||
// Add the memory region to the list of memory to be included.
|
|
||||||
AppMemory app_memory;
|
|
||||||
app_memory.ptr = memory;
|
|
||||||
app_memory.length = kMemorySize;
|
|
||||||
memory_list.push_back(app_memory);
|
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
|
||||||
mappings, memory_list));
|
|
||||||
|
|
||||||
// Read the minidump. Ensure that the memory region is present
|
|
||||||
Minidump minidump(templ);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
|
|
||||||
MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
|
|
||||||
ASSERT_TRUE(dump_memory_list);
|
|
||||||
const MinidumpMemoryRegion* region =
|
|
||||||
dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
|
|
||||||
ASSERT_TRUE(region);
|
|
||||||
|
|
||||||
EXPECT_EQ(kMemoryAddress, region->GetBase());
|
|
||||||
EXPECT_EQ(kMemorySize, region->GetSize());
|
|
||||||
|
|
||||||
// Verify memory contents.
|
|
||||||
EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
|
|
||||||
|
|
||||||
delete[] memory;
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that an invalid thread stack pointer still results in a minidump.
|
|
||||||
TEST(MinidumpWriterTest, InvalidStackPointer) {
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
const pid_t child = fork();
|
|
||||||
if (child == 0) {
|
|
||||||
close(fds[1]);
|
|
||||||
char b;
|
|
||||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
|
|
||||||
close(fds[0]);
|
|
||||||
syscall(__NR_exit);
|
|
||||||
}
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context;
|
|
||||||
|
|
||||||
// This needs a valid context for minidump writing to work, but getting
|
|
||||||
// a useful one from the child is too much work, so just use one from
|
|
||||||
// the parent since the child is just a forked copy anyway.
|
|
||||||
ASSERT_EQ(0, getcontext(&context.context));
|
|
||||||
context.tid = child;
|
|
||||||
|
|
||||||
// Fake the child's stack pointer for its crashing thread. NOTE: This must
|
|
||||||
// be an invalid memory address for the child process (stack or otherwise).
|
|
||||||
// Try 1MB below the current stack.
|
|
||||||
uintptr_t invalid_stack_pointer =
|
|
||||||
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
|
|
||||||
#if defined(__i386)
|
|
||||||
context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
|
|
||||||
#elif defined(__x86_64)
|
|
||||||
context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
|
|
||||||
#elif defined(__ARM_EABI__)
|
|
||||||
context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
context.context.uc_mcontext.sp = invalid_stack_pointer;
|
|
||||||
#elif defined(__mips__)
|
|
||||||
context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
|
|
||||||
invalid_stack_pointer;
|
|
||||||
#else
|
|
||||||
# error "This code has not been ported to your platform yet."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
|
||||||
// NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
|
|
||||||
// presented with an invalid stack pointer.
|
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
|
|
||||||
|
|
||||||
// Read the minidump. Ensure that the memory region is present
|
|
||||||
Minidump minidump(templ);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
|
|
||||||
// TODO(ted.mielczarek,mkrebs): Enable this part of the test once
|
|
||||||
// https://breakpad.appspot.com/413002/ is committed.
|
|
||||||
#if 0
|
|
||||||
// Make sure there's a thread without a stack. NOTE: It's okay if
|
|
||||||
// GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
|
|
||||||
// region problem".
|
|
||||||
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
|
|
||||||
ASSERT_TRUE(dump_thread_list);
|
|
||||||
bool found_empty_stack = false;
|
|
||||||
for (int i = 0; i < dump_thread_list->thread_count(); i++) {
|
|
||||||
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
|
|
||||||
ASSERT_TRUE(thread->thread() != NULL);
|
|
||||||
// When the stack size is zero bytes, GetMemory() returns NULL.
|
|
||||||
if (thread->GetMemory() == NULL) {
|
|
||||||
found_empty_stack = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// NOTE: If you fail this, first make sure that "invalid_stack_pointer"
|
|
||||||
// above is indeed set to an invalid address.
|
|
||||||
ASSERT_TRUE(found_empty_stack);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
close(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that limiting the size of the minidump works.
|
|
||||||
TEST(MinidumpWriterTest, MinidumpSizeLimit) {
|
|
||||||
static const int kNumberOfThreadsInHelperProgram = 40;
|
|
||||||
|
|
||||||
char number_of_threads_arg[3];
|
|
||||||
sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
|
|
||||||
|
|
||||||
string helper_path(GetHelperBinary());
|
|
||||||
if (helper_path.empty()) {
|
|
||||||
FAIL() << "Couldn't find helper binary";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fds[2];
|
|
||||||
ASSERT_NE(-1, pipe(fds));
|
|
||||||
|
|
||||||
pid_t child_pid = fork();
|
|
||||||
if (child_pid == 0) {
|
|
||||||
// In child process.
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
// Pass the pipe fd and the number of threads as arguments.
|
|
||||||
char pipe_fd_string[8];
|
|
||||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
|
||||||
execl(helper_path.c_str(),
|
|
||||||
helper_path.c_str(),
|
|
||||||
pipe_fd_string,
|
|
||||||
number_of_threads_arg,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
close(fds[1]);
|
|
||||||
|
|
||||||
// Wait for all child threads to indicate that they have started
|
|
||||||
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
|
|
||||||
struct pollfd pfd;
|
|
||||||
memset(&pfd, 0, sizeof(pfd));
|
|
||||||
pfd.fd = fds[0];
|
|
||||||
pfd.events = POLLIN | POLLERR;
|
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
|
||||||
ASSERT_EQ(1, r);
|
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN);
|
|
||||||
uint8_t junk;
|
|
||||||
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
|
|
||||||
static_cast<ssize_t>(sizeof(junk)));
|
|
||||||
}
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
// There is a race here because we may stop a child thread before
|
|
||||||
// it is actually running the busy loop. Empirically this sleep
|
|
||||||
// is sufficient to avoid the race.
|
|
||||||
usleep(100000);
|
|
||||||
|
|
||||||
// Child and its threads are ready now.
|
|
||||||
|
|
||||||
|
|
||||||
off_t normal_file_size;
|
|
||||||
int total_normal_stack_size = 0;
|
|
||||||
AutoTempDir temp_dir;
|
|
||||||
|
|
||||||
// First, write a minidump with no size limit.
|
|
||||||
{
|
|
||||||
string normal_dump = temp_dir.path() +
|
|
||||||
"/minidump-writer-unittest.dmp";
|
|
||||||
ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
|
|
||||||
child_pid, NULL, 0,
|
|
||||||
MappingList(), AppMemoryList()));
|
|
||||||
struct stat st;
|
|
||||||
ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
|
|
||||||
ASSERT_GT(st.st_size, 0);
|
|
||||||
normal_file_size = st.st_size;
|
|
||||||
|
|
||||||
Minidump minidump(normal_dump);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
|
|
||||||
ASSERT_TRUE(dump_thread_list);
|
|
||||||
for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
|
|
||||||
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
|
|
||||||
ASSERT_TRUE(thread->thread() != NULL);
|
|
||||||
// When the stack size is zero bytes, GetMemory() returns NULL.
|
|
||||||
MinidumpMemoryRegion* memory = thread->GetMemory();
|
|
||||||
ASSERT_TRUE(memory != NULL);
|
|
||||||
total_normal_stack_size += memory->GetSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second, write a minidump with a size limit big enough to not trigger
|
|
||||||
// anything.
|
|
||||||
{
|
|
||||||
// Set size limit arbitrarily 1MB larger than the normal file size -- such
|
|
||||||
// that the limiting code will not kick in.
|
|
||||||
const off_t minidump_size_limit = normal_file_size + 1024*1024;
|
|
||||||
|
|
||||||
string same_dump = temp_dir.path() +
|
|
||||||
"/minidump-writer-unittest-same.dmp";
|
|
||||||
ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
|
|
||||||
child_pid, NULL, 0,
|
|
||||||
MappingList(), AppMemoryList()));
|
|
||||||
struct stat st;
|
|
||||||
ASSERT_EQ(0, stat(same_dump.c_str(), &st));
|
|
||||||
// Make sure limiting wasn't actually triggered. NOTE: If you fail this,
|
|
||||||
// first make sure that "minidump_size_limit" above is indeed set to a
|
|
||||||
// large enough value -- the limit-checking code in minidump_writer.cc
|
|
||||||
// does just a rough estimate.
|
|
||||||
ASSERT_EQ(normal_file_size, st.st_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Third, write a minidump with a size limit small enough to be triggered.
|
|
||||||
{
|
|
||||||
// Set size limit to some arbitrary amount, such that the limiting code
|
|
||||||
// will kick in. The equation used to set this value was determined by
|
|
||||||
// simply reversing the size-limit logic a little bit in order to pick a
|
|
||||||
// size we know will trigger it. The definition of
|
|
||||||
// kLimitAverageThreadStackLength here was copied from class
|
|
||||||
// MinidumpWriter in minidump_writer.cc.
|
|
||||||
static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
|
|
||||||
off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
|
|
||||||
kLimitAverageThreadStackLength;
|
|
||||||
// If, in reality, each of the threads' stack is *smaller* than
|
|
||||||
// kLimitAverageThreadStackLength, the normal file size could very well be
|
|
||||||
// smaller than the arbitrary limit that was just set. In that case,
|
|
||||||
// either of these numbers should trigger the size-limiting code, but we
|
|
||||||
// might as well pick the smallest.
|
|
||||||
if (normal_file_size < minidump_size_limit)
|
|
||||||
minidump_size_limit = normal_file_size;
|
|
||||||
|
|
||||||
string limit_dump = temp_dir.path() +
|
|
||||||
"/minidump-writer-unittest-limit.dmp";
|
|
||||||
ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
|
|
||||||
child_pid, NULL, 0,
|
|
||||||
MappingList(), AppMemoryList()));
|
|
||||||
struct stat st;
|
|
||||||
ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
|
|
||||||
ASSERT_GT(st.st_size, 0);
|
|
||||||
// Make sure the file size is at least smaller than the original. If this
|
|
||||||
// fails because it's the same size, then the size-limit logic didn't kick
|
|
||||||
// in like it was supposed to.
|
|
||||||
EXPECT_LT(st.st_size, normal_file_size);
|
|
||||||
|
|
||||||
Minidump minidump(limit_dump);
|
|
||||||
ASSERT_TRUE(minidump.Read());
|
|
||||||
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
|
|
||||||
ASSERT_TRUE(dump_thread_list);
|
|
||||||
int total_limit_stack_size = 0;
|
|
||||||
for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
|
|
||||||
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
|
|
||||||
ASSERT_TRUE(thread->thread() != NULL);
|
|
||||||
// When the stack size is zero bytes, GetMemory() returns NULL.
|
|
||||||
MinidumpMemoryRegion* memory = thread->GetMemory();
|
|
||||||
ASSERT_TRUE(memory != NULL);
|
|
||||||
total_limit_stack_size += memory->GetSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure stack size shrunk by at least 1KB per extra thread. The
|
|
||||||
// definition of kLimitBaseThreadCount here was copied from class
|
|
||||||
// MinidumpWriter in minidump_writer.cc.
|
|
||||||
// Note: The 1KB is arbitrary, and assumes that the thread stacks are big
|
|
||||||
// enough to shrink by that much. For example, if each thread stack was
|
|
||||||
// originally only 2KB, the current size-limit logic wouldn't actually
|
|
||||||
// shrink them because that's the size to which it tries to shrink. If
|
|
||||||
// you fail this part of the test due to something like that, the test
|
|
||||||
// logic should probably be improved to account for your situation.
|
|
||||||
const unsigned kLimitBaseThreadCount = 20;
|
|
||||||
const unsigned kMinPerExtraThreadStackReduction = 1024;
|
|
||||||
const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
|
|
||||||
kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
|
|
||||||
EXPECT_LT(total_limit_stack_size,
|
|
||||||
total_normal_stack_size - min_expected_reduction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill the helper program.
|
|
||||||
kill(child_pid, SIGKILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
@ -1,66 +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.
|
|
||||||
|
|
||||||
// minidump_writer_unittest_utils.cc:
|
|
||||||
// Shared routines used by unittests under client/linux/minidump_writer.
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
|
|
||||||
#include "common/linux/safe_readlink.h"
|
|
||||||
#include "common/using_std_string.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
string GetHelperBinary() {
|
|
||||||
string helper_path;
|
|
||||||
char *bindir = getenv("bindir");
|
|
||||||
if (bindir) {
|
|
||||||
helper_path = string(bindir) + "/";
|
|
||||||
} else {
|
|
||||||
// Locate helper binary next to the current binary.
|
|
||||||
char self_path[PATH_MAX];
|
|
||||||
if (!SafeReadLink("/proc/self/exe", self_path)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
helper_path = string(self_path);
|
|
||||||
size_t pos = helper_path.rfind('/');
|
|
||||||
if (pos == string::npos) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
helper_path.erase(pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
helper_path += "linux_dumper_unittest_helper";
|
|
||||||
|
|
||||||
return helper_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,49 +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.
|
|
||||||
|
|
||||||
// minidump_writer_unittest_utils.h:
|
|
||||||
// Shared routines used by unittests under client/linux/minidump_writer.
|
|
||||||
|
|
||||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/using_std_string.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// Returns the full path to linux_dumper_unittest_helper. The full path is
|
|
||||||
// discovered either by using the environment variable "bindir" or by using
|
|
||||||
// the location of the main module of the currently running process.
|
|
||||||
string GetHelperBinary();
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
|
|
@ -1,130 +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_PROC_CPUINFO_READER_H_
|
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// A class for reading /proc/cpuinfo without using fopen/fgets or other
|
|
||||||
// functions which may allocate memory.
|
|
||||||
class ProcCpuInfoReader {
|
|
||||||
public:
|
|
||||||
ProcCpuInfoReader(int fd)
|
|
||||||
: line_reader_(fd), pop_count_(-1) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the next field name, or NULL in case of EOF.
|
|
||||||
// field: (output) Pointer to zero-terminated field name.
|
|
||||||
// Returns true on success, or false on EOF or error (line too long).
|
|
||||||
bool GetNextField(const char** field) {
|
|
||||||
for (;;) {
|
|
||||||
const char* line;
|
|
||||||
unsigned line_len;
|
|
||||||
|
|
||||||
// Try to read next line.
|
|
||||||
if (pop_count_ >= 0) {
|
|
||||||
line_reader_.PopLine(pop_count_);
|
|
||||||
pop_count_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!line_reader_.GetNextLine(&line, &line_len))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pop_count_ = static_cast<int>(line_len);
|
|
||||||
|
|
||||||
const char* line_end = line + line_len;
|
|
||||||
|
|
||||||
// Expected format: <field-name> <space>+ ':' <space> <value>
|
|
||||||
// Note that:
|
|
||||||
// - empty lines happen.
|
|
||||||
// - <field-name> can contain spaces.
|
|
||||||
// - some fields have an empty <value>
|
|
||||||
char* sep = static_cast<char*>(my_memchr(line, ':', line_len));
|
|
||||||
if (sep == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Record the value. Skip leading space after the column to get
|
|
||||||
// its start.
|
|
||||||
const char* val = sep+1;
|
|
||||||
while (val < line_end && my_isspace(*val))
|
|
||||||
val++;
|
|
||||||
|
|
||||||
value_ = val;
|
|
||||||
value_len_ = static_cast<size_t>(line_end - val);
|
|
||||||
|
|
||||||
// Remove trailing spaces before the column to properly 0-terminate
|
|
||||||
// the field name.
|
|
||||||
while (sep > line && my_isspace(sep[-1]))
|
|
||||||
sep--;
|
|
||||||
|
|
||||||
if (sep == line)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// zero-terminate field name.
|
|
||||||
*sep = '\0';
|
|
||||||
|
|
||||||
*field = line;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the field value. This must be called after a succesful
|
|
||||||
// call to GetNextField().
|
|
||||||
const char* GetValue() {
|
|
||||||
assert(value_);
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as GetValue(), but also returns the length in characters of
|
|
||||||
// the value.
|
|
||||||
const char* GetValueAndLen(size_t* length) {
|
|
||||||
assert(value_);
|
|
||||||
*length = value_len_;
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
LineReader line_reader_;
|
|
||||||
int pop_count_;
|
|
||||||
const char* value_;
|
|
||||||
size_t value_len_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
|
|
@ -1,199 +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 "client/linux/minidump_writer/proc_cpuinfo_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
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
typedef testing::Test ProcCpuInfoReaderTest;
|
|
||||||
|
|
||||||
class ScopedTestFile : public AutoTestFile {
|
|
||||||
public:
|
|
||||||
explicit ScopedTestFile(const char* text)
|
|
||||||
: AutoTestFile("proc_cpuinfo_reader", text) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, EmptyFile) {
|
|
||||||
ScopedTestFile file("");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char *field;
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, OneLineTerminated) {
|
|
||||||
ScopedTestFile file("foo : bar\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char *field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, OneLine) {
|
|
||||||
ScopedTestFile file("foo : bar");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char *field;
|
|
||||||
size_t value_len;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len));
|
|
||||||
ASSERT_EQ(3U, value_len);
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) {
|
|
||||||
ScopedTestFile file("foo : bar\nzoo : tut\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("zoo", field);
|
|
||||||
ASSERT_STREQ("tut", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, SkipMalformedLine) {
|
|
||||||
ScopedTestFile file("this line should have a column\nfoo : bar\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) {
|
|
||||||
ScopedTestFile file("\n\nfoo : bar\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, SkipEmptyField) {
|
|
||||||
ScopedTestFile file(" : bar\nzoo : tut\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("zoo", field);
|
|
||||||
ASSERT_STREQ("tut", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) {
|
|
||||||
ScopedTestFile file("foo : bar\n\n\nfoo : bar\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
ASSERT_STREQ("bar", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, FieldWithSpaces) {
|
|
||||||
ScopedTestFile file("foo bar : zoo\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo bar", field);
|
|
||||||
ASSERT_STREQ("zoo", reader.GetValue());
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ProcCpuInfoReaderTest, EmptyValue) {
|
|
||||||
ScopedTestFile file("foo :\n");
|
|
||||||
ASSERT_TRUE(file.IsOk());
|
|
||||||
ProcCpuInfoReader reader(file.GetFd());
|
|
||||||
|
|
||||||
const char* field;
|
|
||||||
ASSERT_TRUE(reader.GetNextField(&field));
|
|
||||||
ASSERT_STREQ("foo", field);
|
|
||||||
size_t value_len;
|
|
||||||
ASSERT_STREQ("", reader.GetValueAndLen(&value_len));
|
|
||||||
ASSERT_EQ(0U, value_len);
|
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextField(&field));
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
// Copyright (c) 2009, 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 "common/linux/google_crashdump_uploader.h"
|
|
||||||
#include "third_party/linux/include/gflags/gflags.h"
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "common/using_std_string.h"
|
|
||||||
|
|
||||||
DEFINE_string(crash_server, "https://clients2.google.com/cr",
|
|
||||||
"The crash server to upload minidumps to.");
|
|
||||||
DEFINE_string(product_name, "",
|
|
||||||
"The product name that the minidump corresponds to.");
|
|
||||||
DEFINE_string(product_version, "",
|
|
||||||
"The version of the product that produced the minidump.");
|
|
||||||
DEFINE_string(client_id, "",
|
|
||||||
"The client GUID");
|
|
||||||
DEFINE_string(minidump_path, "",
|
|
||||||
"The path of the minidump file.");
|
|
||||||
DEFINE_string(ptime, "",
|
|
||||||
"The process uptime in milliseconds.");
|
|
||||||
DEFINE_string(ctime, "",
|
|
||||||
"The cumulative process uptime in milliseconds.");
|
|
||||||
DEFINE_string(email, "",
|
|
||||||
"The user's email address.");
|
|
||||||
DEFINE_string(comments, "",
|
|
||||||
"Extra user comments");
|
|
||||||
DEFINE_string(proxy_host, "",
|
|
||||||
"Proxy host");
|
|
||||||
DEFINE_string(proxy_userpasswd, "",
|
|
||||||
"Proxy username/password in user:pass format.");
|
|
||||||
|
|
||||||
|
|
||||||
bool CheckForRequiredFlagsOrDie() {
|
|
||||||
string error_text = "";
|
|
||||||
if (FLAGS_product_name.empty()) {
|
|
||||||
error_text.append("\nProduct name must be specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FLAGS_product_version.empty()) {
|
|
||||||
error_text.append("\nProduct version must be specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FLAGS_client_id.empty()) {
|
|
||||||
error_text.append("\nClient ID must be specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FLAGS_minidump_path.empty()) {
|
|
||||||
error_text.append("\nMinidump pathname must be specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error_text.empty()) {
|
|
||||||
std::cout << error_text;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
google::InitGoogleLogging(argv[0]);
|
|
||||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
|
||||||
if (!CheckForRequiredFlagsOrDie()) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name,
|
|
||||||
FLAGS_product_version,
|
|
||||||
FLAGS_client_id,
|
|
||||||
FLAGS_ptime,
|
|
||||||
FLAGS_ctime,
|
|
||||||
FLAGS_email,
|
|
||||||
FLAGS_comments,
|
|
||||||
FLAGS_minidump_path,
|
|
||||||
FLAGS_crash_server,
|
|
||||||
FLAGS_proxy_host,
|
|
||||||
FLAGS_proxy_userpasswd);
|
|
||||||
g.Upload(NULL, NULL, NULL);
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
// minidump_file_writer-inl.h: Minidump file writer implementation.
|
|
||||||
//
|
|
||||||
// See minidump_file_writer.h for documentation.
|
|
||||||
|
|
||||||
#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__
|
|
||||||
#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "client/minidump_file_writer.h"
|
|
||||||
#include "google_breakpad/common/minidump_size.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::Allocate() {
|
|
||||||
allocation_state_ = SINGLE_OBJECT;
|
|
||||||
return UntypedMDRVA::Allocate(minidump_size<MDType>::size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::Allocate(size_t additional) {
|
|
||||||
allocation_state_ = SINGLE_OBJECT;
|
|
||||||
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + additional);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) {
|
|
||||||
assert(count);
|
|
||||||
allocation_state_ = ARRAY;
|
|
||||||
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() * count);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count,
|
|
||||||
size_t length) {
|
|
||||||
assert(count && length);
|
|
||||||
allocation_state_ = SINGLE_OBJECT_WITH_ARRAY;
|
|
||||||
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
|
|
||||||
assert(allocation_state_ == ARRAY);
|
|
||||||
return writer_->Copy(
|
|
||||||
static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),
|
|
||||||
item, minidump_size<MDType>::size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
|
|
||||||
const void *src,
|
|
||||||
size_t length) {
|
|
||||||
assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
|
|
||||||
return writer_->Copy(
|
|
||||||
static_cast<MDRVA>(position_ + minidump_size<MDType>::size()
|
|
||||||
+ index * length),
|
|
||||||
src, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MDType>
|
|
||||||
inline bool TypedMDRVA<MDType>::Flush() {
|
|
||||||
return writer_->Copy(position_, &data_, minidump_size<MDType>::size());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__
|
|
@ -1,284 +0,0 @@
|
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
// minidump_file_writer.cc: Minidump file writer implementation.
|
|
||||||
//
|
|
||||||
// See minidump_file_writer.h for documentation.
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "client/minidump_file_writer-inl.h"
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
#include "common/string_conversion.h"
|
|
||||||
#if defined(__linux__) && __linux__
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
|
|
||||||
|
|
||||||
MinidumpFileWriter::MinidumpFileWriter()
|
|
||||||
: file_(-1),
|
|
||||||
close_file_when_destroyed_(true),
|
|
||||||
position_(0),
|
|
||||||
size_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpFileWriter::~MinidumpFileWriter() {
|
|
||||||
if (close_file_when_destroyed_)
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::Open(const char *path) {
|
|
||||||
assert(file_ == -1);
|
|
||||||
#if defined(__linux__) && __linux__
|
|
||||||
file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
|
||||||
#else
|
|
||||||
file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return file_ != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MinidumpFileWriter::SetFile(const int file) {
|
|
||||||
assert(file_ == -1);
|
|
||||||
file_ = file;
|
|
||||||
close_file_when_destroyed_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::Close() {
|
|
||||||
bool result = true;
|
|
||||||
|
|
||||||
if (file_ != -1) {
|
|
||||||
if (-1 == ftruncate(file_, position_)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#if defined(__linux__) && __linux__
|
|
||||||
result = (sys_close(file_) == 0);
|
|
||||||
#else
|
|
||||||
result = (close(file_) == 0);
|
|
||||||
#endif
|
|
||||||
file_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
|
|
||||||
unsigned int length,
|
|
||||||
TypedMDRVA<MDString> *mdstring) {
|
|
||||||
bool result = true;
|
|
||||||
if (sizeof(wchar_t) == sizeof(uint16_t)) {
|
|
||||||
// Shortcut if wchar_t is the same size as MDString's buffer
|
|
||||||
result = mdstring->Copy(str, mdstring->get()->length);
|
|
||||||
} else {
|
|
||||||
uint16_t out[2];
|
|
||||||
int out_idx = 0;
|
|
||||||
|
|
||||||
// Copy the string character by character
|
|
||||||
while (length && result) {
|
|
||||||
UTF32ToUTF16Char(*str, out);
|
|
||||||
if (!out[0])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Process one character at a time
|
|
||||||
--length;
|
|
||||||
++str;
|
|
||||||
|
|
||||||
// Append the one or two UTF-16 characters. The first one will be non-
|
|
||||||
// zero, but the second one may be zero, depending on the conversion from
|
|
||||||
// UTF-32.
|
|
||||||
int out_count = out[1] ? 2 : 1;
|
|
||||||
size_t out_size = sizeof(uint16_t) * out_count;
|
|
||||||
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
|
|
||||||
out_idx += out_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::CopyStringToMDString(const char *str,
|
|
||||||
unsigned int length,
|
|
||||||
TypedMDRVA<MDString> *mdstring) {
|
|
||||||
bool result = true;
|
|
||||||
uint16_t out[2];
|
|
||||||
int out_idx = 0;
|
|
||||||
|
|
||||||
// Copy the string character by character
|
|
||||||
while (length && result) {
|
|
||||||
int conversion_count = UTF8ToUTF16Char(str, length, out);
|
|
||||||
if (!conversion_count)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Move the pointer along based on the nubmer of converted characters
|
|
||||||
length -= conversion_count;
|
|
||||||
str += conversion_count;
|
|
||||||
|
|
||||||
// Append the one or two UTF-16 characters
|
|
||||||
int out_count = out[1] ? 2 : 1;
|
|
||||||
size_t out_size = sizeof(uint16_t) * out_count;
|
|
||||||
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
|
|
||||||
out_idx += out_count;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename CharType>
|
|
||||||
bool MinidumpFileWriter::WriteStringCore(const CharType *str,
|
|
||||||
unsigned int length,
|
|
||||||
MDLocationDescriptor *location) {
|
|
||||||
assert(str);
|
|
||||||
assert(location);
|
|
||||||
// Calculate the mdstring length by either limiting to |length| as passed in
|
|
||||||
// or by finding the location of the NULL character.
|
|
||||||
unsigned int mdstring_length = 0;
|
|
||||||
if (!length)
|
|
||||||
length = INT_MAX;
|
|
||||||
for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
|
|
||||||
;
|
|
||||||
|
|
||||||
// Allocate the string buffer
|
|
||||||
TypedMDRVA<MDString> mdstring(this);
|
|
||||||
if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Set length excluding the NULL and copy the string
|
|
||||||
mdstring.get()->length =
|
|
||||||
static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
|
|
||||||
bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
|
|
||||||
|
|
||||||
// NULL terminate
|
|
||||||
if (result) {
|
|
||||||
uint16_t ch = 0;
|
|
||||||
result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
*location = mdstring.location();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
|
|
||||||
MDLocationDescriptor *location) {
|
|
||||||
return WriteStringCore(str, length, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
|
|
||||||
MDLocationDescriptor *location) {
|
|
||||||
return WriteStringCore(str, length, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
|
|
||||||
MDMemoryDescriptor *output) {
|
|
||||||
assert(src);
|
|
||||||
assert(output);
|
|
||||||
UntypedMDRVA mem(this);
|
|
||||||
|
|
||||||
if (!mem.Allocate(size))
|
|
||||||
return false;
|
|
||||||
if (!mem.Copy(src, mem.size()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
|
|
||||||
output->memory = mem.location();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MDRVA MinidumpFileWriter::Allocate(size_t size) {
|
|
||||||
assert(size);
|
|
||||||
assert(file_ != -1);
|
|
||||||
size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
|
|
||||||
|
|
||||||
if (position_ + aligned_size > size_) {
|
|
||||||
size_t growth = aligned_size;
|
|
||||||
size_t minimal_growth = getpagesize();
|
|
||||||
|
|
||||||
// Ensure that the file grows by at least the size of a memory page
|
|
||||||
if (growth < minimal_growth)
|
|
||||||
growth = minimal_growth;
|
|
||||||
|
|
||||||
size_t new_size = size_ + growth;
|
|
||||||
if (ftruncate(file_, new_size) != 0)
|
|
||||||
return kInvalidMDRVA;
|
|
||||||
|
|
||||||
size_ = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
MDRVA current_position = position_;
|
|
||||||
position_ += static_cast<MDRVA>(aligned_size);
|
|
||||||
|
|
||||||
return current_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
|
|
||||||
assert(src);
|
|
||||||
assert(size);
|
|
||||||
assert(file_ != -1);
|
|
||||||
|
|
||||||
// Ensure that the data will fit in the allocated space
|
|
||||||
if (static_cast<size_t>(size + position) > size_)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Seek and write the data
|
|
||||||
#if defined(__linux__) && __linux__
|
|
||||||
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
|
||||||
if (sys_write(file_, src, size) == size) {
|
|
||||||
#else
|
|
||||||
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
|
||||||
if (write(file_, src, size) == size) {
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UntypedMDRVA::Allocate(size_t size) {
|
|
||||||
assert(size_ == 0);
|
|
||||||
size_ = size;
|
|
||||||
position_ = writer_->Allocate(size_);
|
|
||||||
return position_ != MinidumpFileWriter::kInvalidMDRVA;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
|
|
||||||
assert(src);
|
|
||||||
assert(size);
|
|
||||||
assert(pos + size <= position_ + size_);
|
|
||||||
return writer_->Copy(pos, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,272 +0,0 @@
|
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
// minidump_file_writer.h: Implements file-based minidump generation. It's
|
|
||||||
// intended to be used with the Google Breakpad open source crash handling
|
|
||||||
// project.
|
|
||||||
|
|
||||||
#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__
|
|
||||||
#define CLIENT_MINIDUMP_FILE_WRITER_H__
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
class UntypedMDRVA;
|
|
||||||
template<typename MDType> class TypedMDRVA;
|
|
||||||
|
|
||||||
// The user of this class can Open() a file and add minidump streams, data, and
|
|
||||||
// strings using the definitions in minidump_format.h. Since this class is
|
|
||||||
// expected to be used in a situation where the current process may be
|
|
||||||
// damaged, it will not allocate heap memory.
|
|
||||||
// Sample usage:
|
|
||||||
// MinidumpFileWriter writer;
|
|
||||||
// writer.Open("/tmp/minidump.dmp");
|
|
||||||
// TypedMDRVA<MDRawHeader> header(&writer_);
|
|
||||||
// header.Allocate();
|
|
||||||
// header->get()->signature = MD_HEADER_SIGNATURE;
|
|
||||||
// :
|
|
||||||
// writer.Close();
|
|
||||||
//
|
|
||||||
// An alternative is to use SetFile and provide a file descriptor:
|
|
||||||
// MinidumpFileWriter writer;
|
|
||||||
// writer.SetFile(minidump_fd);
|
|
||||||
// TypedMDRVA<MDRawHeader> header(&writer_);
|
|
||||||
// header.Allocate();
|
|
||||||
// header->get()->signature = MD_HEADER_SIGNATURE;
|
|
||||||
// :
|
|
||||||
// writer.Close();
|
|
||||||
|
|
||||||
class MinidumpFileWriter {
|
|
||||||
public:
|
|
||||||
// Invalid MDRVA (Minidump Relative Virtual Address)
|
|
||||||
// returned on failed allocation
|
|
||||||
static const MDRVA kInvalidMDRVA;
|
|
||||||
|
|
||||||
MinidumpFileWriter();
|
|
||||||
~MinidumpFileWriter();
|
|
||||||
|
|
||||||
// Open |path| as the destination of the minidump data. If |path| already
|
|
||||||
// exists, then Open() will fail.
|
|
||||||
// Return true on success, or false on failure.
|
|
||||||
bool Open(const char *path);
|
|
||||||
|
|
||||||
// Sets the file descriptor |file| as the destination of the minidump data.
|
|
||||||
// Can be used as an alternative to Open() when a file descriptor is
|
|
||||||
// available.
|
|
||||||
// Note that |fd| is not closed when the instance of MinidumpFileWriter is
|
|
||||||
// destroyed.
|
|
||||||
void SetFile(const int file);
|
|
||||||
|
|
||||||
// Close the current file (that was either created when Open was called, or
|
|
||||||
// specified with SetFile).
|
|
||||||
// Return true on success, or false on failure.
|
|
||||||
bool Close();
|
|
||||||
|
|
||||||
// Copy the contents of |str| to a MDString and write it to the file.
|
|
||||||
// |str| is expected to be either UTF-16 or UTF-32 depending on the size
|
|
||||||
// of wchar_t.
|
|
||||||
// Maximum |length| of characters to copy from |str|, or specify 0 to use the
|
|
||||||
// entire NULL terminated string. Copying will stop at the first NULL.
|
|
||||||
// |location| the allocated location
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool WriteString(const wchar_t *str, unsigned int length,
|
|
||||||
MDLocationDescriptor *location);
|
|
||||||
|
|
||||||
// Same as above, except with |str| as a UTF-8 string
|
|
||||||
bool WriteString(const char *str, unsigned int length,
|
|
||||||
MDLocationDescriptor *location);
|
|
||||||
|
|
||||||
// Write |size| bytes starting at |src| into the current position.
|
|
||||||
// Return true on success and set |output| to position, or false on failure
|
|
||||||
bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output);
|
|
||||||
|
|
||||||
// Copies |size| bytes from |src| to |position|
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool Copy(MDRVA position, const void *src, ssize_t size);
|
|
||||||
|
|
||||||
// Return the current position for writing to the minidump
|
|
||||||
inline MDRVA position() const { return position_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class UntypedMDRVA;
|
|
||||||
|
|
||||||
// Allocates an area of |size| bytes.
|
|
||||||
// Returns the position of the allocation, or kInvalidMDRVA if it was
|
|
||||||
// unable to allocate the bytes.
|
|
||||||
MDRVA Allocate(size_t size);
|
|
||||||
|
|
||||||
// The file descriptor for the output file.
|
|
||||||
int file_;
|
|
||||||
|
|
||||||
// Whether |file_| should be closed when the instance is destroyed.
|
|
||||||
bool close_file_when_destroyed_;
|
|
||||||
|
|
||||||
// Current position in buffer
|
|
||||||
MDRVA position_;
|
|
||||||
|
|
||||||
// Current allocated size
|
|
||||||
size_t size_;
|
|
||||||
|
|
||||||
// Copy |length| characters from |str| to |mdstring|. These are distinct
|
|
||||||
// because the underlying MDString is a UTF-16 based string. The wchar_t
|
|
||||||
// variant may need to create a MDString that has more characters than the
|
|
||||||
// source |str|, whereas the UTF-8 variant may coalesce characters to form
|
|
||||||
// a single UTF-16 character.
|
|
||||||
bool CopyStringToMDString(const wchar_t *str, unsigned int length,
|
|
||||||
TypedMDRVA<MDString> *mdstring);
|
|
||||||
bool CopyStringToMDString(const char *str, unsigned int length,
|
|
||||||
TypedMDRVA<MDString> *mdstring);
|
|
||||||
|
|
||||||
// The common templated code for writing a string
|
|
||||||
template <typename CharType>
|
|
||||||
bool WriteStringCore(const CharType *str, unsigned int length,
|
|
||||||
MDLocationDescriptor *location);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Represents an untyped allocated chunk
|
|
||||||
class UntypedMDRVA {
|
|
||||||
public:
|
|
||||||
explicit UntypedMDRVA(MinidumpFileWriter *writer)
|
|
||||||
: writer_(writer),
|
|
||||||
position_(writer->position()),
|
|
||||||
size_(0) {}
|
|
||||||
|
|
||||||
// Allocates |size| bytes. Must not call more than once.
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool Allocate(size_t size);
|
|
||||||
|
|
||||||
// Returns the current position or kInvalidMDRVA if allocation failed
|
|
||||||
inline MDRVA position() const { return position_; }
|
|
||||||
|
|
||||||
// Number of bytes allocated
|
|
||||||
inline size_t size() const { return size_; }
|
|
||||||
|
|
||||||
// Return size and position
|
|
||||||
inline MDLocationDescriptor location() const {
|
|
||||||
MDLocationDescriptor location = { static_cast<uint32_t>(size_),
|
|
||||||
position_ };
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy |size| bytes starting at |src| into the minidump at |position|
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool Copy(MDRVA position, const void *src, size_t size);
|
|
||||||
|
|
||||||
// Copy |size| bytes from |src| to the current position
|
|
||||||
inline bool Copy(const void *src, size_t size) {
|
|
||||||
return Copy(position_, src, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Writer we associate with
|
|
||||||
MinidumpFileWriter *writer_;
|
|
||||||
|
|
||||||
// Position of the start of the data
|
|
||||||
MDRVA position_;
|
|
||||||
|
|
||||||
// Allocated size
|
|
||||||
size_t size_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Represents a Minidump object chunk. Additional memory can be allocated at
|
|
||||||
// the end of the object as a:
|
|
||||||
// - single allocation
|
|
||||||
// - Array of MDType objects
|
|
||||||
// - A MDType object followed by an array
|
|
||||||
template<typename MDType>
|
|
||||||
class TypedMDRVA : public UntypedMDRVA {
|
|
||||||
public:
|
|
||||||
// Constructs an unallocated MDRVA
|
|
||||||
explicit TypedMDRVA(MinidumpFileWriter *writer)
|
|
||||||
: UntypedMDRVA(writer),
|
|
||||||
data_(),
|
|
||||||
allocation_state_(UNALLOCATED) {}
|
|
||||||
|
|
||||||
inline ~TypedMDRVA() {
|
|
||||||
// Ensure that the data_ object is written out
|
|
||||||
if (allocation_state_ != ARRAY)
|
|
||||||
Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address of object data_ of MDType. This is not declared const as the
|
|
||||||
// typical usage will be to access the underlying |data_| object as to
|
|
||||||
// alter its contents.
|
|
||||||
MDType *get() { return &data_; }
|
|
||||||
|
|
||||||
// Allocates minidump_size<MDType>::size() bytes.
|
|
||||||
// Must not call more than once.
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool Allocate();
|
|
||||||
|
|
||||||
// Allocates minidump_size<MDType>::size() + |additional| bytes.
|
|
||||||
// Must not call more than once.
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool Allocate(size_t additional);
|
|
||||||
|
|
||||||
// Allocate an array of |count| elements of MDType.
|
|
||||||
// Must not call more than once.
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool AllocateArray(size_t count);
|
|
||||||
|
|
||||||
// Allocate an array of |count| elements of |size| after object of MDType
|
|
||||||
// Must not call more than once.
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool AllocateObjectAndArray(size_t count, size_t size);
|
|
||||||
|
|
||||||
// Copy |item| to |index|
|
|
||||||
// Must have been allocated using AllocateArray().
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool CopyIndex(unsigned int index, MDType *item);
|
|
||||||
|
|
||||||
// Copy |size| bytes starting at |str| to |index|
|
|
||||||
// Must have been allocated using AllocateObjectAndArray().
|
|
||||||
// Return true on success, or false on failure
|
|
||||||
bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size);
|
|
||||||
|
|
||||||
// Write data_
|
|
||||||
bool Flush();
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum AllocationState {
|
|
||||||
UNALLOCATED = 0,
|
|
||||||
SINGLE_OBJECT,
|
|
||||||
ARRAY,
|
|
||||||
SINGLE_OBJECT_WITH_ARRAY
|
|
||||||
};
|
|
||||||
|
|
||||||
MDType data_;
|
|
||||||
AllocationState allocation_state_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_MINIDUMP_FILE_WRITER_H__
|
|
@ -1,179 +0,0 @@
|
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
// Author: waylonis@google.com (Dan Waylonis)
|
|
||||||
|
|
||||||
/*
|
|
||||||
g++ -I../ ../common/convert_UTF.c \
|
|
||||||
../common/string_conversion.cc \
|
|
||||||
minidump_file_writer.cc \
|
|
||||||
minidump_file_writer_unittest.cc \
|
|
||||||
-o minidump_file_writer_unittest
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "minidump_file_writer-inl.h"
|
|
||||||
|
|
||||||
using google_breakpad::MinidumpFileWriter;
|
|
||||||
|
|
||||||
#define ASSERT_TRUE(cond) \
|
|
||||||
if (!(cond)) { \
|
|
||||||
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
|
|
||||||
return false; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
|
||||||
#define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2))
|
|
||||||
|
|
||||||
struct StringStructure {
|
|
||||||
unsigned long integer_value;
|
|
||||||
MDLocationDescriptor first_string;
|
|
||||||
MDLocationDescriptor second_string;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ArrayStructure {
|
|
||||||
unsigned char char_value;
|
|
||||||
unsigned short short_value;
|
|
||||||
unsigned long long_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned long count;
|
|
||||||
ArrayStructure array[0];
|
|
||||||
} ObjectAndArrayStructure;
|
|
||||||
|
|
||||||
static bool WriteFile(const char *path) {
|
|
||||||
MinidumpFileWriter writer;
|
|
||||||
if (writer.Open(path)) {
|
|
||||||
// Test a single structure
|
|
||||||
google_breakpad::TypedMDRVA<StringStructure> strings(&writer);
|
|
||||||
ASSERT_TRUE(strings.Allocate());
|
|
||||||
strings.get()->integer_value = 0xBEEF;
|
|
||||||
const char *first = "First String";
|
|
||||||
ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string));
|
|
||||||
const wchar_t *second = L"Second String";
|
|
||||||
ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string));
|
|
||||||
|
|
||||||
// Test an array structure
|
|
||||||
google_breakpad::TypedMDRVA<ArrayStructure> array(&writer);
|
|
||||||
unsigned int count = 10;
|
|
||||||
ASSERT_TRUE(array.AllocateArray(count));
|
|
||||||
for (unsigned char i = 0; i < count; ++i) {
|
|
||||||
ArrayStructure local;
|
|
||||||
local.char_value = i;
|
|
||||||
local.short_value = i + 1;
|
|
||||||
local.long_value = i + 2;
|
|
||||||
ASSERT_TRUE(array.CopyIndex(i, &local));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test an object followed by an array
|
|
||||||
google_breakpad::TypedMDRVA<ObjectAndArrayStructure> obj_array(&writer);
|
|
||||||
ASSERT_TRUE(obj_array.AllocateObjectAndArray(count,
|
|
||||||
sizeof(ArrayStructure)));
|
|
||||||
obj_array.get()->count = count;
|
|
||||||
for (unsigned char i = 0; i < count; ++i) {
|
|
||||||
ArrayStructure local;
|
|
||||||
local.char_value = i;
|
|
||||||
local.short_value = i + 1;
|
|
||||||
local.long_value = i + 2;
|
|
||||||
ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return writer.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool CompareFile(const char *path) {
|
|
||||||
unsigned long expected[] = {
|
|
||||||
#if defined(__BIG_ENDIAN__)
|
|
||||||
0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,
|
|
||||||
0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069,
|
|
||||||
0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064,
|
|
||||||
0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002,
|
|
||||||
0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005,
|
|
||||||
0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008,
|
|
||||||
0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b,
|
|
||||||
0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003,
|
|
||||||
0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006,
|
|
||||||
0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009,
|
|
||||||
0x0000000a, 0x0900000a, 0x0000000b, 0x00000000
|
|
||||||
#else
|
|
||||||
0x0000beef, 0x0000001e, 0x00000018, 0x00000020,
|
|
||||||
0x00000038, 0x00000000, 0x00000018, 0x00690046,
|
|
||||||
0x00730072, 0x00200074, 0x00740053, 0x00690072,
|
|
||||||
0x0067006e, 0x00000000, 0x0000001a, 0x00650053,
|
|
||||||
0x006f0063, 0x0064006e, 0x00530020, 0x00720074,
|
|
||||||
0x006e0069, 0x00000067, 0x00011e00, 0x00000002,
|
|
||||||
0x00021e01, 0x00000003, 0x00031e02, 0x00000004,
|
|
||||||
0x00041e03, 0x00000005, 0x00051e04, 0x00000006,
|
|
||||||
0x00061e05, 0x00000007, 0x00071e06, 0x00000008,
|
|
||||||
0x00081e07, 0x00000009, 0x00091e08, 0x0000000a,
|
|
||||||
0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00,
|
|
||||||
0x00000002, 0x00021c01, 0x00000003, 0x00031c02,
|
|
||||||
0x00000004, 0x00041c03, 0x00000005, 0x00051c04,
|
|
||||||
0x00000006, 0x00061c05, 0x00000007, 0x00071c06,
|
|
||||||
0x00000008, 0x00081c07, 0x00000009, 0x00091c08,
|
|
||||||
0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
size_t expected_byte_count = sizeof(expected);
|
|
||||||
int fd = open(path, O_RDONLY, 0600);
|
|
||||||
void *buffer = malloc(expected_byte_count);
|
|
||||||
ASSERT_NE(fd, -1);
|
|
||||||
ASSERT_TRUE(buffer);
|
|
||||||
ASSERT_EQ(read(fd, buffer, expected_byte_count),
|
|
||||||
static_cast<ssize_t>(expected_byte_count));
|
|
||||||
|
|
||||||
char *b1, *b2;
|
|
||||||
b1 = reinterpret_cast<char*>(buffer);
|
|
||||||
b2 = reinterpret_cast<char*>(expected);
|
|
||||||
while (*b1 == *b2) {
|
|
||||||
b1++;
|
|
||||||
b2++;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer));
|
|
||||||
|
|
||||||
ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool RunTests() {
|
|
||||||
const char *path = "/tmp/minidump_file_writer_unittest.dmp";
|
|
||||||
ASSERT_TRUE(WriteFile(path));
|
|
||||||
ASSERT_TRUE(CompareFile(path));
|
|
||||||
unlink(path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int main(int argc, const char *argv[]) {
|
|
||||||
return RunTests() ? 0 : 1;
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
# Copyright 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
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
{
|
|
||||||
'includes': [
|
|
||||||
'../../build/common.gypi',
|
|
||||||
],
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'common',
|
|
||||||
'type': 'static_library',
|
|
||||||
'include_dirs': [
|
|
||||||
'<(DEPTH)',
|
|
||||||
],
|
|
||||||
'direct_dependent_settings': {
|
|
||||||
'include_dirs': [
|
|
||||||
'<(DEPTH)',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'sources': [
|
|
||||||
'<(DEPTH)/common/windows/guid_string.cc',
|
|
||||||
'<(DEPTH)/common/windows/guid_string.h',
|
|
||||||
'<(DEPTH)/common/windows/http_upload.cc',
|
|
||||||
'<(DEPTH)/common/windows/http_upload.h',
|
|
||||||
'<(DEPTH)/common/windows/string_utils.cc',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
|
||||||
#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// Automatically enters the critical section in the constructor and leaves
|
|
||||||
// the critical section in the destructor.
|
|
||||||
class AutoCriticalSection {
|
|
||||||
public:
|
|
||||||
// Creates a new instance with the given critical section object
|
|
||||||
// and enters the critical section immediately.
|
|
||||||
explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs), taken_(false) {
|
|
||||||
assert(cs_);
|
|
||||||
Acquire();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor: leaves the critical section.
|
|
||||||
~AutoCriticalSection() {
|
|
||||||
if (taken_) {
|
|
||||||
Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enters the critical section. Recursive Acquire() calls are not allowed.
|
|
||||||
void Acquire() {
|
|
||||||
assert(!taken_);
|
|
||||||
EnterCriticalSection(cs_);
|
|
||||||
taken_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leaves the critical section. The caller should not call Release() unless
|
|
||||||
// the critical seciton has been entered already.
|
|
||||||
void Release() {
|
|
||||||
assert(taken_);
|
|
||||||
taken_ = false;
|
|
||||||
LeaveCriticalSection(cs_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Disable copy ctor and operator=.
|
|
||||||
AutoCriticalSection(const AutoCriticalSection&);
|
|
||||||
AutoCriticalSection& operator=(const AutoCriticalSection&);
|
|
||||||
|
|
||||||
CRITICAL_SECTION* cs_;
|
|
||||||
bool taken_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
|
@ -1,181 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
|
||||||
#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include "common/windows/string_utils-inl.h"
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// Name/value pair for custom client information.
|
|
||||||
struct CustomInfoEntry {
|
|
||||||
// Maximum length for name and value for client custom info.
|
|
||||||
static const int kNameMaxLength = 64;
|
|
||||||
static const int kValueMaxLength = 64;
|
|
||||||
|
|
||||||
CustomInfoEntry() {
|
|
||||||
// Putting name and value in initializer list makes VC++ show warning 4351.
|
|
||||||
set_name(NULL);
|
|
||||||
set_value(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomInfoEntry(const wchar_t* name_arg, const wchar_t* value_arg) {
|
|
||||||
set_name(name_arg);
|
|
||||||
set_value(value_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_name(const wchar_t* name_arg) {
|
|
||||||
if (!name_arg) {
|
|
||||||
name[0] = L'\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
WindowsStringUtils::safe_wcscpy(name, kNameMaxLength, name_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_value(const wchar_t* value_arg) {
|
|
||||||
if (!value_arg) {
|
|
||||||
value[0] = L'\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowsStringUtils::safe_wcscpy(value, kValueMaxLength, value_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(const wchar_t* name_arg, const wchar_t* value_arg) {
|
|
||||||
set_name(name_arg);
|
|
||||||
set_value(value_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t name[kNameMaxLength];
|
|
||||||
wchar_t value[kValueMaxLength];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constants for the protocol between client and the server.
|
|
||||||
|
|
||||||
// Tags sent with each message indicating the purpose of
|
|
||||||
// the message.
|
|
||||||
enum MessageTag {
|
|
||||||
MESSAGE_TAG_NONE = 0,
|
|
||||||
MESSAGE_TAG_REGISTRATION_REQUEST = 1,
|
|
||||||
MESSAGE_TAG_REGISTRATION_RESPONSE = 2,
|
|
||||||
MESSAGE_TAG_REGISTRATION_ACK = 3,
|
|
||||||
MESSAGE_TAG_UPLOAD_REQUEST = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CustomClientInfo {
|
|
||||||
const CustomInfoEntry* entries;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Message structure for IPC between crash client and crash server.
|
|
||||||
struct ProtocolMessage {
|
|
||||||
ProtocolMessage()
|
|
||||||
: tag(MESSAGE_TAG_NONE),
|
|
||||||
id(0),
|
|
||||||
dump_type(MiniDumpNormal),
|
|
||||||
thread_id(0),
|
|
||||||
exception_pointers(NULL),
|
|
||||||
assert_info(NULL),
|
|
||||||
custom_client_info(),
|
|
||||||
dump_request_handle(NULL),
|
|
||||||
dump_generated_handle(NULL),
|
|
||||||
server_alive_handle(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates an instance with the given parameters.
|
|
||||||
ProtocolMessage(MessageTag arg_tag,
|
|
||||||
DWORD arg_id,
|
|
||||||
MINIDUMP_TYPE arg_dump_type,
|
|
||||||
DWORD* arg_thread_id,
|
|
||||||
EXCEPTION_POINTERS** arg_exception_pointers,
|
|
||||||
MDRawAssertionInfo* arg_assert_info,
|
|
||||||
const CustomClientInfo& custom_info,
|
|
||||||
HANDLE arg_dump_request_handle,
|
|
||||||
HANDLE arg_dump_generated_handle,
|
|
||||||
HANDLE arg_server_alive)
|
|
||||||
: tag(arg_tag),
|
|
||||||
id(arg_id),
|
|
||||||
dump_type(arg_dump_type),
|
|
||||||
thread_id(arg_thread_id),
|
|
||||||
exception_pointers(arg_exception_pointers),
|
|
||||||
assert_info(arg_assert_info),
|
|
||||||
custom_client_info(custom_info),
|
|
||||||
dump_request_handle(arg_dump_request_handle),
|
|
||||||
dump_generated_handle(arg_dump_generated_handle),
|
|
||||||
server_alive_handle(arg_server_alive) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tag in the message.
|
|
||||||
MessageTag tag;
|
|
||||||
|
|
||||||
// The id for this message. This may be either a process id or a crash id
|
|
||||||
// depending on the type of message.
|
|
||||||
DWORD id;
|
|
||||||
|
|
||||||
// Dump type requested.
|
|
||||||
MINIDUMP_TYPE dump_type;
|
|
||||||
|
|
||||||
// Client thread id pointer.
|
|
||||||
DWORD* thread_id;
|
|
||||||
|
|
||||||
// Exception information.
|
|
||||||
EXCEPTION_POINTERS** exception_pointers;
|
|
||||||
|
|
||||||
// Assert information in case of an invalid parameter or
|
|
||||||
// pure call failure.
|
|
||||||
MDRawAssertionInfo* assert_info;
|
|
||||||
|
|
||||||
// Custom client information.
|
|
||||||
CustomClientInfo custom_client_info;
|
|
||||||
|
|
||||||
// Handle to signal the crash event.
|
|
||||||
HANDLE dump_request_handle;
|
|
||||||
|
|
||||||
// Handle to check if server is done generating crash.
|
|
||||||
HANDLE dump_generated_handle;
|
|
||||||
|
|
||||||
// Handle to a mutex that becomes signaled (WAIT_ABANDONED)
|
|
||||||
// if server process goes down.
|
|
||||||
HANDLE server_alive_handle;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Disable copy ctor and operator=.
|
|
||||||
ProtocolMessage(const ProtocolMessage& msg);
|
|
||||||
ProtocolMessage& operator=(const ProtocolMessage& msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
|
@ -1,58 +0,0 @@
|
|||||||
=========================================================================
|
|
||||||
State machine transitions for the Crash Generation Server
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
|
|
|
||||||
STATE | ACTIONS
|
|
||||||
|
|
|
||||||
=========================================================================
|
|
||||||
ERROR | Clean up resources used to serve clients.
|
|
||||||
| Always remain in ERROR state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
INITIAL | Connect to the pipe asynchronously.
|
|
||||||
| If connection is successfully queued up asynchronously,
|
|
||||||
| go into CONNECTING state.
|
|
||||||
| If connection is done synchronously, go into CONNECTED
|
|
||||||
| state.
|
|
||||||
| For any unexpected problems, go into ERROR state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
CONNECTING | Get the result of async connection request.
|
|
||||||
| If I/O is still incomplete, remain in the CONNECTING
|
|
||||||
| state.
|
|
||||||
| If connection is complete, go into CONNECTED state.
|
|
||||||
| For any unexpected problems, go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
CONNECTED | Read from the pipe asynchronously.
|
|
||||||
| If read request is successfully queued up asynchronously,
|
|
||||||
| go into READING state.
|
|
||||||
| For any unexpected problems, go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
READING | Get the result of async read request.
|
|
||||||
| If read is done, go into READ_DONE state.
|
|
||||||
| For any unexpected problems, go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
READ_DONE | Register the client, prepare the reply and write the
|
|
||||||
| reply to the pipe asynchronously.
|
|
||||||
| If write request is successfully queued up asynchronously,
|
|
||||||
| go into WRITING state.
|
|
||||||
| For any unexpected problems, go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
WRITING | Get the result of the async write request.
|
|
||||||
| If write is done, go into WRITE_DONE state.
|
|
||||||
| For any unexpected problems, go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
WRITE_DONE | Read from the pipe asynchronously (for an ACK).
|
|
||||||
| If read request is successfully queued up asynchonously,
|
|
||||||
| go into READING_ACK state.
|
|
||||||
| For any unexpected problems, go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
READING_ACK | Get the result of the async read request.
|
|
||||||
| If read is done, perform action for successful client
|
|
||||||
| connection.
|
|
||||||
| Go into DISCONNECTING state.
|
|
||||||
-------------------------------------------------------------------------
|
|
||||||
DISCONNECTING | Disconnect from the pipe, reset the event and go into
|
|
||||||
| INITIAL state and signal the event again. If anything
|
|
||||||
| fails, go into ERROR state.
|
|
||||||
=========================================================================
|
|
@ -1,223 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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/windows/crash_generation/client_info.h"
|
|
||||||
#include "client/windows/common/ipc_protocol.h"
|
|
||||||
|
|
||||||
static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime";
|
|
||||||
static const size_t kMaxCustomInfoEntries = 4096;
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
ClientInfo::ClientInfo(CrashGenerationServer* crash_server,
|
|
||||||
DWORD pid,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
DWORD* thread_id,
|
|
||||||
EXCEPTION_POINTERS** ex_info,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
const CustomClientInfo& custom_client_info)
|
|
||||||
: crash_server_(crash_server),
|
|
||||||
pid_(pid),
|
|
||||||
dump_type_(dump_type),
|
|
||||||
ex_info_(ex_info),
|
|
||||||
assert_info_(assert_info),
|
|
||||||
custom_client_info_(custom_client_info),
|
|
||||||
thread_id_(thread_id),
|
|
||||||
process_handle_(NULL),
|
|
||||||
dump_requested_handle_(NULL),
|
|
||||||
dump_generated_handle_(NULL),
|
|
||||||
dump_request_wait_handle_(NULL),
|
|
||||||
process_exit_wait_handle_(NULL),
|
|
||||||
crash_id_(NULL) {
|
|
||||||
GetSystemTimeAsFileTime(&start_time_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientInfo::Initialize() {
|
|
||||||
process_handle_ = OpenProcess(GENERIC_ALL, FALSE, pid_);
|
|
||||||
if (!process_handle_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The crash_id will be the low order word of the process creation time.
|
|
||||||
FILETIME creation_time, exit_time, kernel_time, user_time;
|
|
||||||
if (GetProcessTimes(process_handle_, &creation_time, &exit_time,
|
|
||||||
&kernel_time, &user_time)) {
|
|
||||||
start_time_ = creation_time;
|
|
||||||
}
|
|
||||||
crash_id_ = start_time_.dwLowDateTime;
|
|
||||||
|
|
||||||
dump_requested_handle_ = CreateEvent(NULL, // Security attributes.
|
|
||||||
TRUE, // Manual reset.
|
|
||||||
FALSE, // Initial state.
|
|
||||||
NULL); // Name.
|
|
||||||
if (!dump_requested_handle_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dump_generated_handle_ = CreateEvent(NULL, // Security attributes.
|
|
||||||
TRUE, // Manual reset.
|
|
||||||
FALSE, // Initial state.
|
|
||||||
NULL); // Name.
|
|
||||||
return dump_generated_handle_ != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientInfo::UnregisterDumpRequestWaitAndBlockUntilNoPending() {
|
|
||||||
if (dump_request_wait_handle_) {
|
|
||||||
// Wait for callbacks that might already be running to finish.
|
|
||||||
UnregisterWaitEx(dump_request_wait_handle_, INVALID_HANDLE_VALUE);
|
|
||||||
dump_request_wait_handle_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientInfo::UnregisterProcessExitWait(bool block_until_no_pending) {
|
|
||||||
if (process_exit_wait_handle_) {
|
|
||||||
if (block_until_no_pending) {
|
|
||||||
// Wait for the callback that might already be running to finish.
|
|
||||||
UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE);
|
|
||||||
} else {
|
|
||||||
UnregisterWait(process_exit_wait_handle_);
|
|
||||||
}
|
|
||||||
process_exit_wait_handle_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientInfo::~ClientInfo() {
|
|
||||||
// Waiting for the callback to finish here is safe because ClientInfo's are
|
|
||||||
// never destroyed from the dump request handling callback.
|
|
||||||
UnregisterDumpRequestWaitAndBlockUntilNoPending();
|
|
||||||
|
|
||||||
// This is a little tricky because ClientInfo's may be destroyed by the same
|
|
||||||
// callback (OnClientEnd) and waiting for it to finish will cause a deadlock.
|
|
||||||
// Regardless of this complication, wait for any running callbacks to finish
|
|
||||||
// so that the common case is properly handled. In order to avoid deadlocks,
|
|
||||||
// the OnClientEnd callback must call UnregisterProcessExitWait(false)
|
|
||||||
// before deleting the ClientInfo.
|
|
||||||
UnregisterProcessExitWait(true);
|
|
||||||
|
|
||||||
if (process_handle_) {
|
|
||||||
CloseHandle(process_handle_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump_requested_handle_) {
|
|
||||||
CloseHandle(dump_requested_handle_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump_generated_handle_) {
|
|
||||||
CloseHandle(dump_generated_handle_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const {
|
|
||||||
SIZE_T bytes_count = 0;
|
|
||||||
if (!ReadProcessMemory(process_handle_,
|
|
||||||
ex_info_,
|
|
||||||
ex_info,
|
|
||||||
sizeof(*ex_info),
|
|
||||||
&bytes_count)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes_count == sizeof(*ex_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientInfo::GetClientThreadId(DWORD* thread_id) const {
|
|
||||||
SIZE_T bytes_count = 0;
|
|
||||||
if (!ReadProcessMemory(process_handle_,
|
|
||||||
thread_id_,
|
|
||||||
thread_id,
|
|
||||||
sizeof(*thread_id),
|
|
||||||
&bytes_count)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes_count == sizeof(*thread_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientInfo::SetProcessUptime() {
|
|
||||||
FILETIME now = {0};
|
|
||||||
GetSystemTimeAsFileTime(&now);
|
|
||||||
|
|
||||||
ULARGE_INTEGER time_start;
|
|
||||||
time_start.HighPart = start_time_.dwHighDateTime;
|
|
||||||
time_start.LowPart = start_time_.dwLowDateTime;
|
|
||||||
|
|
||||||
ULARGE_INTEGER time_now;
|
|
||||||
time_now.HighPart = now.dwHighDateTime;
|
|
||||||
time_now.LowPart = now.dwLowDateTime;
|
|
||||||
|
|
||||||
// Calculate the delay and convert it from 100-nanoseconds to milliseconds.
|
|
||||||
__int64 delay = (time_now.QuadPart - time_start.QuadPart) / 10 / 1000;
|
|
||||||
|
|
||||||
// Convert it to a string.
|
|
||||||
wchar_t* value = custom_info_entries_.get()[custom_client_info_.count].value;
|
|
||||||
_i64tow_s(delay, value, CustomInfoEntry::kValueMaxLength, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientInfo::PopulateCustomInfo() {
|
|
||||||
if (custom_client_info_.count > kMaxCustomInfoEntries)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
SIZE_T bytes_count = 0;
|
|
||||||
SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count;
|
|
||||||
|
|
||||||
// If the scoped array for custom info already has an array, it will be
|
|
||||||
// the same size as what we need. This is because the number of custom info
|
|
||||||
// entries is always the same. So allocate memory only if scoped array has
|
|
||||||
// a NULL pointer.
|
|
||||||
if (!custom_info_entries_.get()) {
|
|
||||||
// Allocate an extra entry for reporting uptime for the client process.
|
|
||||||
custom_info_entries_.reset(
|
|
||||||
new CustomInfoEntry[custom_client_info_.count + 1]);
|
|
||||||
// Use the last element in the array for uptime.
|
|
||||||
custom_info_entries_.get()[custom_client_info_.count].set_name(
|
|
||||||
kCustomInfoProcessUptimeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ReadProcessMemory(process_handle_,
|
|
||||||
custom_client_info_.entries,
|
|
||||||
custom_info_entries_.get(),
|
|
||||||
read_count,
|
|
||||||
&bytes_count)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetProcessUptime();
|
|
||||||
return (bytes_count == read_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomClientInfo ClientInfo::GetCustomInfo() const {
|
|
||||||
CustomClientInfo custom_info;
|
|
||||||
custom_info.entries = custom_info_entries_.get();
|
|
||||||
// Add 1 to the count from the client process to account for extra entry for
|
|
||||||
// process uptime.
|
|
||||||
custom_info.count = custom_client_info_.count + 1;
|
|
||||||
return custom_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,177 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
|
||||||
#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
#include "client/windows/common/ipc_protocol.h"
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
class CrashGenerationServer;
|
|
||||||
|
|
||||||
// Abstraction for a crash client process.
|
|
||||||
class ClientInfo {
|
|
||||||
public:
|
|
||||||
// Creates an instance with the given values. Gets the process
|
|
||||||
// handle for the given process id and creates necessary event
|
|
||||||
// objects.
|
|
||||||
ClientInfo(CrashGenerationServer* crash_server,
|
|
||||||
DWORD pid,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
DWORD* thread_id,
|
|
||||||
EXCEPTION_POINTERS** ex_info,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
const CustomClientInfo& custom_client_info);
|
|
||||||
|
|
||||||
~ClientInfo();
|
|
||||||
|
|
||||||
CrashGenerationServer* crash_server() const { return crash_server_; }
|
|
||||||
DWORD pid() const { return pid_; }
|
|
||||||
MINIDUMP_TYPE dump_type() const { return dump_type_; }
|
|
||||||
EXCEPTION_POINTERS** ex_info() const { return ex_info_; }
|
|
||||||
MDRawAssertionInfo* assert_info() const { return assert_info_; }
|
|
||||||
DWORD* thread_id() const { return thread_id_; }
|
|
||||||
HANDLE process_handle() const { return process_handle_; }
|
|
||||||
HANDLE dump_requested_handle() const { return dump_requested_handle_; }
|
|
||||||
HANDLE dump_generated_handle() const { return dump_generated_handle_; }
|
|
||||||
DWORD crash_id() const { return crash_id_; }
|
|
||||||
const CustomClientInfo& custom_client_info() const {
|
|
||||||
return custom_client_info_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_dump_request_wait_handle(HANDLE value) {
|
|
||||||
dump_request_wait_handle_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_process_exit_wait_handle(HANDLE value) {
|
|
||||||
process_exit_wait_handle_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister the dump request wait operation and wait for all callbacks
|
|
||||||
// that might already be running to complete before returning.
|
|
||||||
void UnregisterDumpRequestWaitAndBlockUntilNoPending();
|
|
||||||
|
|
||||||
// Unregister the process exit wait operation. If block_until_no_pending is
|
|
||||||
// true, wait for all callbacks that might already be running to complete
|
|
||||||
// before returning.
|
|
||||||
void UnregisterProcessExitWait(bool block_until_no_pending);
|
|
||||||
|
|
||||||
bool Initialize();
|
|
||||||
bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const;
|
|
||||||
bool GetClientThreadId(DWORD* thread_id) const;
|
|
||||||
|
|
||||||
// Reads the custom information from the client process address space.
|
|
||||||
bool PopulateCustomInfo();
|
|
||||||
|
|
||||||
// Returns the client custom information.
|
|
||||||
CustomClientInfo GetCustomInfo() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Calcualtes the uptime for the client process, converts it to a string and
|
|
||||||
// stores it in the last entry of client custom info.
|
|
||||||
void SetProcessUptime();
|
|
||||||
|
|
||||||
// Crash generation server.
|
|
||||||
CrashGenerationServer* crash_server_;
|
|
||||||
|
|
||||||
// Client process ID.
|
|
||||||
DWORD pid_;
|
|
||||||
|
|
||||||
// Dump type requested by the client.
|
|
||||||
MINIDUMP_TYPE dump_type_;
|
|
||||||
|
|
||||||
// Address of an EXCEPTION_POINTERS* variable in the client
|
|
||||||
// process address space that will point to an instance of
|
|
||||||
// EXCEPTION_POINTERS containing information about crash.
|
|
||||||
//
|
|
||||||
// WARNING: Do not dereference these pointers as they are pointers
|
|
||||||
// in the address space of another process.
|
|
||||||
EXCEPTION_POINTERS** ex_info_;
|
|
||||||
|
|
||||||
// Address of an instance of MDRawAssertionInfo in the client
|
|
||||||
// process address space that will contain information about
|
|
||||||
// non-exception related crashes like invalid parameter assertion
|
|
||||||
// failures and pure calls.
|
|
||||||
//
|
|
||||||
// WARNING: Do not dereference these pointers as they are pointers
|
|
||||||
// in the address space of another process.
|
|
||||||
MDRawAssertionInfo* assert_info_;
|
|
||||||
|
|
||||||
// Custom information about the client.
|
|
||||||
CustomClientInfo custom_client_info_;
|
|
||||||
|
|
||||||
// Contains the custom client info entries read from the client process
|
|
||||||
// memory. This will be populated only if the method GetClientCustomInfo
|
|
||||||
// is called.
|
|
||||||
scoped_array<CustomInfoEntry> custom_info_entries_;
|
|
||||||
|
|
||||||
// Address of a variable in the client process address space that
|
|
||||||
// will contain the thread id of the crashing client thread.
|
|
||||||
//
|
|
||||||
// WARNING: Do not dereference these pointers as they are pointers
|
|
||||||
// in the address space of another process.
|
|
||||||
DWORD* thread_id_;
|
|
||||||
|
|
||||||
// Client process handle.
|
|
||||||
HANDLE process_handle_;
|
|
||||||
|
|
||||||
// Dump request event handle.
|
|
||||||
HANDLE dump_requested_handle_;
|
|
||||||
|
|
||||||
// Dump generated event handle.
|
|
||||||
HANDLE dump_generated_handle_;
|
|
||||||
|
|
||||||
// Wait handle for dump request event.
|
|
||||||
HANDLE dump_request_wait_handle_;
|
|
||||||
|
|
||||||
// Wait handle for process exit event.
|
|
||||||
HANDLE process_exit_wait_handle_;
|
|
||||||
|
|
||||||
// Time when the client process started. It is used to determine the uptime
|
|
||||||
// for the client process when it signals a crash.
|
|
||||||
FILETIME start_time_;
|
|
||||||
|
|
||||||
// The crash id which can be used to request an upload. This will be the
|
|
||||||
// value of the low order dword of the process creation time for the process
|
|
||||||
// being dumped.
|
|
||||||
DWORD crash_id_;
|
|
||||||
|
|
||||||
// Disallow copy ctor and operator=.
|
|
||||||
ClientInfo(const ClientInfo& client_info);
|
|
||||||
ClientInfo& operator=(const ClientInfo& client_info);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
|
@ -1,63 +0,0 @@
|
|||||||
# Copyright 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
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
{
|
|
||||||
'includes': [
|
|
||||||
'../../../build/common.gypi',
|
|
||||||
],
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'crash_generation_server',
|
|
||||||
'type': 'static_library',
|
|
||||||
'sources': [
|
|
||||||
'client_info.cc',
|
|
||||||
'crash_generation_server.cc',
|
|
||||||
'minidump_generator.cc',
|
|
||||||
'client_info.h',
|
|
||||||
'crash_generation_client.h',
|
|
||||||
'crash_generation_server.h',
|
|
||||||
'minidump_generator.h',
|
|
||||||
],
|
|
||||||
'dependencies': [
|
|
||||||
'../breakpad_client.gyp:common'
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'target_name': 'crash_generation_client',
|
|
||||||
'type': 'static_library',
|
|
||||||
'include_dirs': [
|
|
||||||
'<(DEPTH)',
|
|
||||||
],
|
|
||||||
'sources': [
|
|
||||||
'crash_generation_client.h',
|
|
||||||
'crash_generation_client.cc',
|
|
||||||
'crash_generation_server.h',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
@ -1,405 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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/windows/crash_generation/crash_generation_client.h"
|
|
||||||
#include <cassert>
|
|
||||||
#include <utility>
|
|
||||||
#include "client/windows/common/ipc_protocol.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
const int kPipeBusyWaitTimeoutMs = 2000;
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
const DWORD kWaitForServerTimeoutMs = INFINITE;
|
|
||||||
#else
|
|
||||||
const DWORD kWaitForServerTimeoutMs = 15000;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const int kPipeConnectMaxAttempts = 2;
|
|
||||||
|
|
||||||
const DWORD kPipeDesiredAccess = FILE_READ_DATA |
|
|
||||||
FILE_WRITE_DATA |
|
|
||||||
FILE_WRITE_ATTRIBUTES;
|
|
||||||
|
|
||||||
const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
|
|
||||||
SECURITY_SQOS_PRESENT;
|
|
||||||
|
|
||||||
const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
|
|
||||||
|
|
||||||
const size_t kWaitEventCount = 2;
|
|
||||||
|
|
||||||
// This function is orphan for production code. It can be used
|
|
||||||
// for debugging to help repro some scenarios like the client
|
|
||||||
// is slow in writing to the pipe after connecting, the client
|
|
||||||
// is slow in reading from the pipe after writing, etc. The parameter
|
|
||||||
// overlapped below is not used and it is present to match the signature
|
|
||||||
// of this function to TransactNamedPipe Win32 API. Uncomment if needed
|
|
||||||
// for debugging.
|
|
||||||
/**
|
|
||||||
static bool TransactNamedPipeDebugHelper(HANDLE pipe,
|
|
||||||
const void* in_buffer,
|
|
||||||
DWORD in_size,
|
|
||||||
void* out_buffer,
|
|
||||||
DWORD out_size,
|
|
||||||
DWORD* bytes_count,
|
|
||||||
LPOVERLAPPED) {
|
|
||||||
// Uncomment the next sleep to create a gap before writing
|
|
||||||
// to pipe.
|
|
||||||
// Sleep(5000);
|
|
||||||
|
|
||||||
if (!WriteFile(pipe,
|
|
||||||
in_buffer,
|
|
||||||
in_size,
|
|
||||||
bytes_count,
|
|
||||||
NULL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uncomment the next sleep to create a gap between write
|
|
||||||
// and read.
|
|
||||||
// Sleep(5000);
|
|
||||||
|
|
||||||
return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE;
|
|
||||||
}
|
|
||||||
**/
|
|
||||||
|
|
||||||
CrashGenerationClient::CrashGenerationClient(
|
|
||||||
const wchar_t* pipe_name,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
const CustomClientInfo* custom_info)
|
|
||||||
: pipe_name_(pipe_name),
|
|
||||||
pipe_handle_(NULL),
|
|
||||||
dump_type_(dump_type),
|
|
||||||
thread_id_(0),
|
|
||||||
server_process_id_(0),
|
|
||||||
crash_event_(NULL),
|
|
||||||
crash_generated_(NULL),
|
|
||||||
server_alive_(NULL),
|
|
||||||
exception_pointers_(NULL),
|
|
||||||
custom_info_() {
|
|
||||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
|
||||||
if (custom_info) {
|
|
||||||
custom_info_ = *custom_info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CrashGenerationClient::CrashGenerationClient(
|
|
||||||
HANDLE pipe_handle,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
const CustomClientInfo* custom_info)
|
|
||||||
: pipe_name_(),
|
|
||||||
pipe_handle_(pipe_handle),
|
|
||||||
dump_type_(dump_type),
|
|
||||||
thread_id_(0),
|
|
||||||
server_process_id_(0),
|
|
||||||
crash_event_(NULL),
|
|
||||||
crash_generated_(NULL),
|
|
||||||
server_alive_(NULL),
|
|
||||||
exception_pointers_(NULL),
|
|
||||||
custom_info_() {
|
|
||||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
|
||||||
if (custom_info) {
|
|
||||||
custom_info_ = *custom_info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CrashGenerationClient::~CrashGenerationClient() {
|
|
||||||
if (crash_event_) {
|
|
||||||
CloseHandle(crash_event_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crash_generated_) {
|
|
||||||
CloseHandle(crash_generated_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server_alive_) {
|
|
||||||
CloseHandle(server_alive_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Performs the registration step with the server process.
|
|
||||||
// The registration step involves communicating with the server
|
|
||||||
// via a named pipe. The client sends the following pieces of
|
|
||||||
// data to the server:
|
|
||||||
//
|
|
||||||
// * Message tag indicating the client is requesting registration.
|
|
||||||
// * Process id of the client process.
|
|
||||||
// * Address of a DWORD variable in the client address space
|
|
||||||
// that will contain the thread id of the client thread that
|
|
||||||
// caused the crash.
|
|
||||||
// * Address of a EXCEPTION_POINTERS* variable in the client
|
|
||||||
// address space that will point to an instance of EXCEPTION_POINTERS
|
|
||||||
// when the crash happens.
|
|
||||||
// * Address of an instance of MDRawAssertionInfo that will contain
|
|
||||||
// relevant information in case of non-exception crashes like assertion
|
|
||||||
// failures and pure calls.
|
|
||||||
//
|
|
||||||
// In return the client expects the following information from the server:
|
|
||||||
//
|
|
||||||
// * Message tag indicating successful registration.
|
|
||||||
// * Server process id.
|
|
||||||
// * Handle to an object that client can signal to request dump
|
|
||||||
// generation from the server.
|
|
||||||
// * Handle to an object that client can wait on after requesting
|
|
||||||
// dump generation for the server to finish dump generation.
|
|
||||||
// * Handle to a mutex object that client can wait on to make sure
|
|
||||||
// server is still alive.
|
|
||||||
//
|
|
||||||
// If any step of the expected behavior mentioned above fails, the
|
|
||||||
// registration step is not considered successful and hence out-of-process
|
|
||||||
// dump generation service is not available.
|
|
||||||
//
|
|
||||||
// Returns true if the registration is successful; false otherwise.
|
|
||||||
bool CrashGenerationClient::Register() {
|
|
||||||
if (IsRegistered()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE pipe = ConnectToServer();
|
|
||||||
if (!pipe) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = RegisterClient(pipe);
|
|
||||||
CloseHandle(pipe);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::RequestUpload(DWORD crash_id) {
|
|
||||||
HANDLE pipe = ConnectToServer();
|
|
||||||
if (!pipe) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomClientInfo custom_info = {NULL, 0};
|
|
||||||
ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id,
|
|
||||||
static_cast<MINIDUMP_TYPE>(NULL), NULL, NULL, NULL,
|
|
||||||
custom_info, NULL, NULL, NULL);
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0;
|
|
||||||
|
|
||||||
CloseHandle(pipe);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE CrashGenerationClient::ConnectToServer() {
|
|
||||||
HANDLE pipe = ConnectToPipe(pipe_name_.c_str(),
|
|
||||||
kPipeDesiredAccess,
|
|
||||||
kPipeFlagsAndAttributes);
|
|
||||||
if (!pipe) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD mode = kPipeMode;
|
|
||||||
if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) {
|
|
||||||
CloseHandle(pipe);
|
|
||||||
pipe = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
|
|
||||||
ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST,
|
|
||||||
GetCurrentProcessId(),
|
|
||||||
dump_type_,
|
|
||||||
&thread_id_,
|
|
||||||
&exception_pointers_,
|
|
||||||
&assert_info_,
|
|
||||||
custom_info_,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
ProtocolMessage reply;
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
// The call to TransactNamedPipe below can be changed to a call
|
|
||||||
// to TransactNamedPipeDebugHelper to help repro some scenarios.
|
|
||||||
// For details see comments for TransactNamedPipeDebugHelper.
|
|
||||||
if (!TransactNamedPipe(pipe,
|
|
||||||
&msg,
|
|
||||||
sizeof(msg),
|
|
||||||
&reply,
|
|
||||||
sizeof(ProtocolMessage),
|
|
||||||
&bytes_count,
|
|
||||||
NULL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ValidateResponse(reply)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtocolMessage ack_msg;
|
|
||||||
ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK;
|
|
||||||
|
|
||||||
if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
crash_event_ = reply.dump_request_handle;
|
|
||||||
crash_generated_ = reply.dump_generated_handle;
|
|
||||||
server_alive_ = reply.server_alive_handle;
|
|
||||||
server_process_id_ = reply.id;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name,
|
|
||||||
DWORD pipe_access,
|
|
||||||
DWORD flags_attrs) {
|
|
||||||
if (pipe_handle_) {
|
|
||||||
HANDLE t = pipe_handle_;
|
|
||||||
pipe_handle_ = NULL;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
|
|
||||||
HANDLE pipe = CreateFile(pipe_name,
|
|
||||||
pipe_access,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
flags_attrs,
|
|
||||||
NULL);
|
|
||||||
if (pipe != INVALID_HANDLE_VALUE) {
|
|
||||||
return pipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot continue retrying if error is something other than
|
|
||||||
// ERROR_PIPE_BUSY.
|
|
||||||
if (GetLastError() != ERROR_PIPE_BUSY) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot continue retrying if wait on pipe fails.
|
|
||||||
if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::ValidateResponse(
|
|
||||||
const ProtocolMessage& msg) const {
|
|
||||||
return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) &&
|
|
||||||
(msg.id != 0) &&
|
|
||||||
(msg.dump_request_handle != NULL) &&
|
|
||||||
(msg.dump_generated_handle != NULL) &&
|
|
||||||
(msg.server_alive_handle != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::IsRegistered() const {
|
|
||||||
return crash_event_ != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
|
|
||||||
MDRawAssertionInfo* assert_info) {
|
|
||||||
if (!IsRegistered()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
exception_pointers_ = ex_info;
|
|
||||||
thread_id_ = GetCurrentThreadId();
|
|
||||||
|
|
||||||
if (assert_info) {
|
|
||||||
memcpy(&assert_info_, assert_info, sizeof(assert_info_));
|
|
||||||
} else {
|
|
||||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalCrashEventAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
|
|
||||||
return RequestDump(ex_info, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
|
|
||||||
return RequestDump(NULL, assert_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationClient::SignalCrashEventAndWait() {
|
|
||||||
assert(crash_event_);
|
|
||||||
assert(crash_generated_);
|
|
||||||
assert(server_alive_);
|
|
||||||
|
|
||||||
// Reset the dump generated event before signaling the crash
|
|
||||||
// event so that the server can set the dump generated event
|
|
||||||
// once it is done generating the event.
|
|
||||||
if (!ResetEvent(crash_generated_)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SetEvent(crash_event_)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};
|
|
||||||
|
|
||||||
DWORD result = WaitForMultipleObjects(kWaitEventCount,
|
|
||||||
wait_handles,
|
|
||||||
FALSE,
|
|
||||||
kWaitForServerTimeoutMs);
|
|
||||||
|
|
||||||
// Crash dump was successfully generated only if the server
|
|
||||||
// signaled the crash generated event.
|
|
||||||
return result == WAIT_OBJECT_0;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE CrashGenerationClient::DuplicatePipeToClientProcess(const wchar_t* pipe_name,
|
|
||||||
HANDLE hProcess) {
|
|
||||||
for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
|
|
||||||
HANDLE local_pipe = CreateFile(pipe_name, kPipeDesiredAccess,
|
|
||||||
0, NULL, OPEN_EXISTING,
|
|
||||||
kPipeFlagsAndAttributes, NULL);
|
|
||||||
if (local_pipe != INVALID_HANDLE_VALUE) {
|
|
||||||
HANDLE remotePipe = INVALID_HANDLE_VALUE;
|
|
||||||
if (DuplicateHandle(GetCurrentProcess(), local_pipe,
|
|
||||||
hProcess, &remotePipe, 0, FALSE,
|
|
||||||
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
|
|
||||||
return remotePipe;
|
|
||||||
} else {
|
|
||||||
return INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot continue retrying if the error wasn't a busy pipe.
|
|
||||||
if (GetLastError() != ERROR_PIPE_BUSY) {
|
|
||||||
return INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
|
|
||||||
return INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,182 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
||||||
#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include "client/windows/common/ipc_protocol.h"
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
struct CustomClientInfo;
|
|
||||||
|
|
||||||
// Abstraction of client-side implementation of out of process
|
|
||||||
// crash generation.
|
|
||||||
//
|
|
||||||
// The process that desires to have out-of-process crash dump
|
|
||||||
// generation service can use this class in the following way:
|
|
||||||
//
|
|
||||||
// * Create an instance.
|
|
||||||
// * Call Register method so that the client tries to register
|
|
||||||
// with the server process and check the return value. If
|
|
||||||
// registration is not successful, out-of-process crash dump
|
|
||||||
// generation will not be available
|
|
||||||
// * Request dump generation by calling either of the two
|
|
||||||
// overloaded RequestDump methods - one in case of exceptions
|
|
||||||
// and the other in case of assertion failures
|
|
||||||
//
|
|
||||||
// Note that it is the responsibility of the client code of
|
|
||||||
// this class to set the unhandled exception filter with the
|
|
||||||
// system by calling the SetUnhandledExceptionFilter function
|
|
||||||
// and the client code should explicitly request dump generation.
|
|
||||||
class CrashGenerationClient {
|
|
||||||
public:
|
|
||||||
CrashGenerationClient(const wchar_t* pipe_name,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
const CustomClientInfo* custom_info);
|
|
||||||
|
|
||||||
CrashGenerationClient(HANDLE pipe_handle,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
const CustomClientInfo* custom_info);
|
|
||||||
|
|
||||||
~CrashGenerationClient();
|
|
||||||
|
|
||||||
// Registers the client process with the crash server.
|
|
||||||
//
|
|
||||||
// Returns true if the registration is successful; false otherwise.
|
|
||||||
bool Register();
|
|
||||||
|
|
||||||
// Requests the crash server to upload a previous dump with the
|
|
||||||
// given crash id.
|
|
||||||
bool RequestUpload(DWORD crash_id);
|
|
||||||
|
|
||||||
bool RequestDump(EXCEPTION_POINTERS* ex_info,
|
|
||||||
MDRawAssertionInfo* assert_info);
|
|
||||||
|
|
||||||
// Requests the crash server to generate a dump with the given
|
|
||||||
// exception information.
|
|
||||||
//
|
|
||||||
// Returns true if the dump was successful; false otherwise. Note that
|
|
||||||
// if the registration step was not performed or it was not successful,
|
|
||||||
// false will be returned.
|
|
||||||
bool RequestDump(EXCEPTION_POINTERS* ex_info);
|
|
||||||
|
|
||||||
// Requests the crash server to generate a dump with the given
|
|
||||||
// assertion information.
|
|
||||||
//
|
|
||||||
// Returns true if the dump was successful; false otherwise. Note that
|
|
||||||
// if the registration step was not performed or it was not successful,
|
|
||||||
// false will be returned.
|
|
||||||
bool RequestDump(MDRawAssertionInfo* assert_info);
|
|
||||||
|
|
||||||
// If the crash generation client is running in a sandbox that prevents it
|
|
||||||
// from opening the named pipe directly, the server process may open the
|
|
||||||
// handle and duplicate it into the client process with this helper method.
|
|
||||||
// Returns INVALID_HANDLE_VALUE on failure. The process must have been opened
|
|
||||||
// with the PROCESS_DUP_HANDLE access right.
|
|
||||||
static HANDLE DuplicatePipeToClientProcess(const wchar_t* pipe_name,
|
|
||||||
HANDLE hProcess);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Connects to the appropriate pipe and sets the pipe handle state.
|
|
||||||
//
|
|
||||||
// Returns the pipe handle if everything goes well; otherwise Returns NULL.
|
|
||||||
HANDLE ConnectToServer();
|
|
||||||
|
|
||||||
// Performs a handshake with the server over the given pipe which should be
|
|
||||||
// already connected to the server.
|
|
||||||
//
|
|
||||||
// Returns true if handshake with the server was successful; false otherwise.
|
|
||||||
bool RegisterClient(HANDLE pipe);
|
|
||||||
|
|
||||||
// Validates the given server response.
|
|
||||||
bool ValidateResponse(const ProtocolMessage& msg) const;
|
|
||||||
|
|
||||||
// Returns true if the registration step succeeded; false otherwise.
|
|
||||||
bool IsRegistered() const;
|
|
||||||
|
|
||||||
// Connects to the given named pipe with given parameters.
|
|
||||||
//
|
|
||||||
// Returns true if the connection is successful; false otherwise.
|
|
||||||
HANDLE ConnectToPipe(const wchar_t* pipe_name,
|
|
||||||
DWORD pipe_access,
|
|
||||||
DWORD flags_attrs);
|
|
||||||
|
|
||||||
// Signals the crash event and wait for the server to generate crash.
|
|
||||||
bool SignalCrashEventAndWait();
|
|
||||||
|
|
||||||
// Pipe name to use to talk to server.
|
|
||||||
std::wstring pipe_name_;
|
|
||||||
|
|
||||||
// Pipe handle duplicated from server process. Only valid before
|
|
||||||
// Register is called.
|
|
||||||
HANDLE pipe_handle_;
|
|
||||||
|
|
||||||
// Custom client information
|
|
||||||
CustomClientInfo custom_info_;
|
|
||||||
|
|
||||||
// Type of dump to generate.
|
|
||||||
MINIDUMP_TYPE dump_type_;
|
|
||||||
|
|
||||||
// Event to signal in case of a crash.
|
|
||||||
HANDLE crash_event_;
|
|
||||||
|
|
||||||
// Handle to wait on after signaling a crash for the server
|
|
||||||
// to finish generating crash dump.
|
|
||||||
HANDLE crash_generated_;
|
|
||||||
|
|
||||||
// Handle to a mutex that will become signaled with WAIT_ABANDONED
|
|
||||||
// if the server process goes down.
|
|
||||||
HANDLE server_alive_;
|
|
||||||
|
|
||||||
// Server process id.
|
|
||||||
DWORD server_process_id_;
|
|
||||||
|
|
||||||
// Id of the thread that caused the crash.
|
|
||||||
DWORD thread_id_;
|
|
||||||
|
|
||||||
// Exception pointers for an exception crash.
|
|
||||||
EXCEPTION_POINTERS* exception_pointers_;
|
|
||||||
|
|
||||||
// Assertion info for an invalid parameter or pure call crash.
|
|
||||||
MDRawAssertionInfo assert_info_;
|
|
||||||
|
|
||||||
// Disable copy ctor and operator=.
|
|
||||||
CrashGenerationClient(const CrashGenerationClient& crash_client);
|
|
||||||
CrashGenerationClient& operator=(const CrashGenerationClient& crash_client);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
@ -1,931 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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/windows/crash_generation/crash_generation_server.h"
|
|
||||||
#include <windows.h>
|
|
||||||
#include <cassert>
|
|
||||||
#include <list>
|
|
||||||
#include "client/windows/common/auto_critical_section.h"
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
|
|
||||||
#include "client/windows/crash_generation/client_info.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// Output buffer size.
|
|
||||||
static const size_t kOutBufferSize = 64;
|
|
||||||
|
|
||||||
// Input buffer size.
|
|
||||||
static const size_t kInBufferSize = 64;
|
|
||||||
|
|
||||||
// Access flags for the client on the dump request event.
|
|
||||||
static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE;
|
|
||||||
|
|
||||||
// Access flags for the client on the dump generated event.
|
|
||||||
static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE |
|
|
||||||
SYNCHRONIZE;
|
|
||||||
|
|
||||||
// Access flags for the client on the mutex.
|
|
||||||
static const DWORD kMutexAccess = SYNCHRONIZE;
|
|
||||||
|
|
||||||
// Attribute flags for the pipe.
|
|
||||||
static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE |
|
|
||||||
PIPE_ACCESS_DUPLEX |
|
|
||||||
FILE_FLAG_OVERLAPPED;
|
|
||||||
|
|
||||||
// Mode for the pipe.
|
|
||||||
static const DWORD kPipeMode = PIPE_TYPE_MESSAGE |
|
|
||||||
PIPE_READMODE_MESSAGE |
|
|
||||||
PIPE_WAIT;
|
|
||||||
|
|
||||||
// For pipe I/O, execute the callback in the wait thread itself,
|
|
||||||
// since the callback does very little work. The callback executes
|
|
||||||
// the code for one of the states of the server state machine and
|
|
||||||
// the code for all of the states perform async I/O and hence
|
|
||||||
// finish very quickly.
|
|
||||||
static const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD;
|
|
||||||
|
|
||||||
// Dump request threads will, most likely, generate dumps. That may
|
|
||||||
// take some time to finish, so specify WT_EXECUTELONGFUNCTION flag.
|
|
||||||
static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD |
|
|
||||||
WT_EXECUTELONGFUNCTION;
|
|
||||||
|
|
||||||
static bool IsClientRequestValid(const ProtocolMessage& msg) {
|
|
||||||
return msg.tag == MESSAGE_TAG_UPLOAD_REQUEST ||
|
|
||||||
(msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST &&
|
|
||||||
msg.id != 0 &&
|
|
||||||
msg.thread_id != NULL &&
|
|
||||||
msg.exception_pointers != NULL &&
|
|
||||||
msg.assert_info != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
static bool CheckForIOIncomplete(bool success) {
|
|
||||||
// We should never get an I/O incomplete since we should not execute this
|
|
||||||
// unless the operation has finished and the overlapped event is signaled. If
|
|
||||||
// we do get INCOMPLETE, we have a bug in our code.
|
|
||||||
return success ? false : (GetLastError() == ERROR_IO_INCOMPLETE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer(
|
|
||||||
const std::wstring& pipe_name,
|
|
||||||
SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
|
||||||
OnClientConnectedCallback connect_callback,
|
|
||||||
void* connect_context,
|
|
||||||
OnClientDumpRequestCallback dump_callback,
|
|
||||||
void* dump_context,
|
|
||||||
OnClientExitedCallback exit_callback,
|
|
||||||
void* exit_context,
|
|
||||||
OnClientUploadRequestCallback upload_request_callback,
|
|
||||||
void* upload_context,
|
|
||||||
bool generate_dumps,
|
|
||||||
const std::wstring* dump_path)
|
|
||||||
: pipe_name_(pipe_name),
|
|
||||||
pipe_sec_attrs_(pipe_sec_attrs),
|
|
||||||
pipe_(NULL),
|
|
||||||
pipe_wait_handle_(NULL),
|
|
||||||
server_alive_handle_(NULL),
|
|
||||||
connect_callback_(connect_callback),
|
|
||||||
connect_context_(connect_context),
|
|
||||||
dump_callback_(dump_callback),
|
|
||||||
dump_context_(dump_context),
|
|
||||||
exit_callback_(exit_callback),
|
|
||||||
exit_context_(exit_context),
|
|
||||||
upload_request_callback_(upload_request_callback),
|
|
||||||
upload_context_(upload_context),
|
|
||||||
generate_dumps_(generate_dumps),
|
|
||||||
dump_path_(dump_path ? *dump_path : L""),
|
|
||||||
server_state_(IPC_SERVER_STATE_UNINITIALIZED),
|
|
||||||
shutting_down_(false),
|
|
||||||
overlapped_(),
|
|
||||||
client_info_(NULL),
|
|
||||||
pre_fetch_custom_info_(true) {
|
|
||||||
InitializeCriticalSection(&sync_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should never be called from the OnPipeConnected callback.
|
|
||||||
// Otherwise the UnregisterWaitEx call below will cause a deadlock.
|
|
||||||
CrashGenerationServer::~CrashGenerationServer() {
|
|
||||||
// New scope to release the lock automatically.
|
|
||||||
{
|
|
||||||
// Make sure no clients are added or removed beyond this point.
|
|
||||||
// Before adding or removing any clients, the critical section
|
|
||||||
// must be entered and the shutting_down_ flag checked. The
|
|
||||||
// critical section is then exited only after the clients_ list
|
|
||||||
// modifications are done and the list is in a consistent state.
|
|
||||||
AutoCriticalSection lock(&sync_);
|
|
||||||
|
|
||||||
// Indicate to existing threads that server is shutting down.
|
|
||||||
shutting_down_ = true;
|
|
||||||
}
|
|
||||||
// No one will modify the clients_ list beyond this point -
|
|
||||||
// not even from another thread.
|
|
||||||
|
|
||||||
// Even if there are no current worker threads running, it is possible that
|
|
||||||
// an I/O request is pending on the pipe right now but not yet done.
|
|
||||||
// In fact, it's very likely this is the case unless we are in an ERROR
|
|
||||||
// state. If we don't wait for the pending I/O to be done, then when the I/O
|
|
||||||
// completes, it may write to invalid memory. AppVerifier will flag this
|
|
||||||
// problem too. So we disconnect from the pipe and then wait for the server
|
|
||||||
// to get into error state so that the pending I/O will fail and get
|
|
||||||
// cleared.
|
|
||||||
DisconnectNamedPipe(pipe_);
|
|
||||||
int num_tries = 100;
|
|
||||||
while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) {
|
|
||||||
Sleep(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister wait on the pipe.
|
|
||||||
if (pipe_wait_handle_) {
|
|
||||||
// Wait for already executing callbacks to finish.
|
|
||||||
UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the pipe to avoid further client connections.
|
|
||||||
if (pipe_) {
|
|
||||||
CloseHandle(pipe_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request all ClientInfo objects to unregister all waits.
|
|
||||||
// No need to enter the critical section because no one is allowed to modify
|
|
||||||
// the clients_ list once the shutting_down_ flag is set.
|
|
||||||
std::list<ClientInfo*>::iterator iter;
|
|
||||||
for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
|
|
||||||
ClientInfo* client_info = *iter;
|
|
||||||
// Unregister waits. Wait for already executing callbacks to finish.
|
|
||||||
// Unregister the client process exit wait first and only then unregister
|
|
||||||
// the dump request wait. The reason is that the OnClientExit callback
|
|
||||||
// also unregisters the dump request wait and such a race (doing the same
|
|
||||||
// unregistration from two threads) is undesirable.
|
|
||||||
client_info->UnregisterProcessExitWait(true);
|
|
||||||
client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending();
|
|
||||||
|
|
||||||
// Destroying the ClientInfo here is safe because all wait operations for
|
|
||||||
// this ClientInfo were unregistered and no pending or running callbacks
|
|
||||||
// for this ClientInfo can possible exist (block_until_no_pending option
|
|
||||||
// was used).
|
|
||||||
delete client_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server_alive_handle_) {
|
|
||||||
// Release the mutex before closing the handle so that clients requesting
|
|
||||||
// dumps wait for a long time for the server to generate a dump.
|
|
||||||
ReleaseMutex(server_alive_handle_);
|
|
||||||
CloseHandle(server_alive_handle_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlapped_.hEvent) {
|
|
||||||
CloseHandle(overlapped_.hEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteCriticalSection(&sync_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationServer::Start() {
|
|
||||||
if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
server_state_ = IPC_SERVER_STATE_INITIAL;
|
|
||||||
|
|
||||||
server_alive_handle_ = CreateMutex(NULL, TRUE, NULL);
|
|
||||||
if (!server_alive_handle_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event to signal the client connection and pipe reads and writes.
|
|
||||||
overlapped_.hEvent = CreateEvent(NULL, // Security descriptor.
|
|
||||||
TRUE, // Manual reset.
|
|
||||||
FALSE, // Initially nonsignaled.
|
|
||||||
NULL); // Name.
|
|
||||||
if (!overlapped_.hEvent) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register a callback with the thread pool for the client connection.
|
|
||||||
if (!RegisterWaitForSingleObject(&pipe_wait_handle_,
|
|
||||||
overlapped_.hEvent,
|
|
||||||
OnPipeConnected,
|
|
||||||
this,
|
|
||||||
INFINITE,
|
|
||||||
kPipeIOThreadFlags)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pipe_ = CreateNamedPipe(pipe_name_.c_str(),
|
|
||||||
kPipeAttr,
|
|
||||||
kPipeMode,
|
|
||||||
1,
|
|
||||||
kOutBufferSize,
|
|
||||||
kInBufferSize,
|
|
||||||
0,
|
|
||||||
pipe_sec_attrs_);
|
|
||||||
if (pipe_ == INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kick-start the state machine. This will initiate an asynchronous wait
|
|
||||||
// for client connections.
|
|
||||||
if (!SetEvent(overlapped_.hEvent)) {
|
|
||||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in error state, it's because we failed to start listening.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the server thread serving clients ever gets into the
|
|
||||||
// ERROR state, reset the event, close the pipe and remain
|
|
||||||
// in the error state forever. Error state means something
|
|
||||||
// that we didn't account for has happened, and it's dangerous
|
|
||||||
// to do anything unknowingly.
|
|
||||||
void CrashGenerationServer::HandleErrorState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_ERROR);
|
|
||||||
|
|
||||||
// If the server is shutting down anyway, don't clean up
|
|
||||||
// here since shut down process will clean up.
|
|
||||||
if (shutting_down_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pipe_wait_handle_) {
|
|
||||||
UnregisterWait(pipe_wait_handle_);
|
|
||||||
pipe_wait_handle_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pipe_) {
|
|
||||||
CloseHandle(pipe_);
|
|
||||||
pipe_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlapped_.hEvent) {
|
|
||||||
CloseHandle(overlapped_.hEvent);
|
|
||||||
overlapped_.hEvent = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving clients is in the INITIAL state,
|
|
||||||
// try to connect to the pipe asynchronously. If the connection
|
|
||||||
// finishes synchronously, directly go into the CONNECTED state;
|
|
||||||
// otherwise go into the CONNECTING state. For any problems, go
|
|
||||||
// into the ERROR state.
|
|
||||||
void CrashGenerationServer::HandleInitialState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_INITIAL);
|
|
||||||
|
|
||||||
if (!ResetEvent(overlapped_.hEvent)) {
|
|
||||||
EnterErrorState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE;
|
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
// From MSDN, it is not clear that when ConnectNamedPipe is used
|
|
||||||
// in an overlapped mode, will it ever return non-zero value, and
|
|
||||||
// if so, in what cases.
|
|
||||||
assert(!success);
|
|
||||||
|
|
||||||
switch (error_code) {
|
|
||||||
case ERROR_IO_PENDING:
|
|
||||||
EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ERROR_PIPE_CONNECTED:
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
EnterErrorState();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the clients is in the CONNECTING state,
|
|
||||||
// try to get the result of the asynchronous connection request using
|
|
||||||
// the OVERLAPPED object. If the result indicates the connection is done,
|
|
||||||
// go into the CONNECTED state. If the result indicates I/O is still
|
|
||||||
// INCOMPLETE, remain in the CONNECTING state. For any problems,
|
|
||||||
// go into the DISCONNECTING state.
|
|
||||||
void CrashGenerationServer::HandleConnectingState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_CONNECTING);
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = GetOverlappedResult(pipe_,
|
|
||||||
&overlapped_,
|
|
||||||
&bytes_count,
|
|
||||||
FALSE) != FALSE;
|
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
|
|
||||||
} else if (error_code != ERROR_IO_INCOMPLETE) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
} else {
|
|
||||||
// remain in CONNECTING state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the clients is in the CONNECTED state,
|
|
||||||
// try to issue an asynchronous read from the pipe. If read completes
|
|
||||||
// synchronously or if I/O is pending then go into the READING state.
|
|
||||||
// For any problems, go into the DISCONNECTING state.
|
|
||||||
void CrashGenerationServer::HandleConnectedState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_CONNECTED);
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
memset(&msg_, 0, sizeof(msg_));
|
|
||||||
bool success = ReadFile(pipe_,
|
|
||||||
&msg_,
|
|
||||||
sizeof(msg_),
|
|
||||||
&bytes_count,
|
|
||||||
&overlapped_) != FALSE;
|
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
// Note that the asynchronous read issued above can finish before the
|
|
||||||
// code below executes. But, it is okay to change state after issuing
|
|
||||||
// the asynchronous read. This is because even if the asynchronous read
|
|
||||||
// is done, the callback for it would not be executed until the current
|
|
||||||
// thread finishes its execution.
|
|
||||||
if (success || error_code == ERROR_IO_PENDING) {
|
|
||||||
EnterStateWhenSignaled(IPC_SERVER_STATE_READING);
|
|
||||||
} else {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the clients is in the READING state,
|
|
||||||
// try to get the result of the async read. If async read is done,
|
|
||||||
// go into the READ_DONE state. For any problems, go into the
|
|
||||||
// DISCONNECTING state.
|
|
||||||
void CrashGenerationServer::HandleReadingState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_READING);
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = GetOverlappedResult(pipe_,
|
|
||||||
&overlapped_,
|
|
||||||
&bytes_count,
|
|
||||||
FALSE) != FALSE;
|
|
||||||
if (success && bytes_count == sizeof(ProtocolMessage)) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!CheckForIOIncomplete(success));
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the client is in the READ_DONE state,
|
|
||||||
// validate the client's request message, register the client by
|
|
||||||
// creating appropriate objects and prepare the response. Then try to
|
|
||||||
// write the response to the pipe asynchronously. If that succeeds,
|
|
||||||
// go into the WRITING state. For any problems, go into the DISCONNECTING
|
|
||||||
// state.
|
|
||||||
void CrashGenerationServer::HandleReadDoneState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_READ_DONE);
|
|
||||||
|
|
||||||
if (!IsClientRequestValid(msg_)) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg_.tag == MESSAGE_TAG_UPLOAD_REQUEST) {
|
|
||||||
if (upload_request_callback_)
|
|
||||||
upload_request_callback_(upload_context_, msg_.id);
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_ptr<ClientInfo> client_info(
|
|
||||||
new ClientInfo(this,
|
|
||||||
msg_.id,
|
|
||||||
msg_.dump_type,
|
|
||||||
msg_.thread_id,
|
|
||||||
msg_.exception_pointers,
|
|
||||||
msg_.assert_info,
|
|
||||||
msg_.custom_client_info));
|
|
||||||
|
|
||||||
if (!client_info->Initialize()) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issues an asynchronous WriteFile call if successful.
|
|
||||||
// Iff successful, assigns ownership of the client_info pointer to the server
|
|
||||||
// instance, in which case we must be sure not to free it in this function.
|
|
||||||
if (!RespondToClient(client_info.get())) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only valid as long as it can be found in the clients_ list
|
|
||||||
client_info_ = client_info.release();
|
|
||||||
|
|
||||||
// Note that the asynchronous write issued by RespondToClient function
|
|
||||||
// can finish before the code below executes. But it is okay to change
|
|
||||||
// state after issuing the asynchronous write. This is because even if
|
|
||||||
// the asynchronous write is done, the callback for it would not be
|
|
||||||
// executed until the current thread finishes its execution.
|
|
||||||
EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the clients is in the WRITING state,
|
|
||||||
// try to get the result of the async write. If the async write is done,
|
|
||||||
// go into the WRITE_DONE state. For any problems, go into the
|
|
||||||
// DISONNECTING state.
|
|
||||||
void CrashGenerationServer::HandleWritingState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_WRITING);
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = GetOverlappedResult(pipe_,
|
|
||||||
&overlapped_,
|
|
||||||
&bytes_count,
|
|
||||||
FALSE) != FALSE;
|
|
||||||
if (success) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!CheckForIOIncomplete(success));
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the clients is in the WRITE_DONE state,
|
|
||||||
// try to issue an async read on the pipe. If the read completes synchronously
|
|
||||||
// or if I/O is still pending then go into the READING_ACK state. For any
|
|
||||||
// issues, go into the DISCONNECTING state.
|
|
||||||
void CrashGenerationServer::HandleWriteDoneState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE);
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = ReadFile(pipe_,
|
|
||||||
&msg_,
|
|
||||||
sizeof(msg_),
|
|
||||||
&bytes_count,
|
|
||||||
&overlapped_) != FALSE;
|
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_READING_ACK);
|
|
||||||
} else if (error_code == ERROR_IO_PENDING) {
|
|
||||||
EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK);
|
|
||||||
} else {
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the clients is in the READING_ACK state,
|
|
||||||
// try to get result of async read. Go into the DISCONNECTING state.
|
|
||||||
void CrashGenerationServer::HandleReadingAckState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_READING_ACK);
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = GetOverlappedResult(pipe_,
|
|
||||||
&overlapped_,
|
|
||||||
&bytes_count,
|
|
||||||
FALSE) != FALSE;
|
|
||||||
if (success) {
|
|
||||||
// The connection handshake with the client is now complete; perform
|
|
||||||
// the callback.
|
|
||||||
if (connect_callback_) {
|
|
||||||
// Note that there is only a single copy of the ClientInfo of the
|
|
||||||
// currently connected client. However it is being referenced from
|
|
||||||
// two different places:
|
|
||||||
// - the client_info_ member
|
|
||||||
// - the clients_ list
|
|
||||||
// The lifetime of this ClientInfo depends on the lifetime of the
|
|
||||||
// client process - basically it can go away at any time.
|
|
||||||
// However, as long as it is referenced by the clients_ list it
|
|
||||||
// is guaranteed to be valid. Enter the critical section and check
|
|
||||||
// to see whether the client_info_ can be found in the list.
|
|
||||||
// If found, execute the callback and only then leave the critical
|
|
||||||
// section.
|
|
||||||
AutoCriticalSection lock(&sync_);
|
|
||||||
|
|
||||||
bool client_is_still_alive = false;
|
|
||||||
std::list<ClientInfo*>::iterator iter;
|
|
||||||
for (iter = clients_.begin(); iter != clients_.end(); ++iter) {
|
|
||||||
if (client_info_ == *iter) {
|
|
||||||
client_is_still_alive = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client_is_still_alive) {
|
|
||||||
connect_callback_(connect_context_, client_info_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(!CheckForIOIncomplete(success));
|
|
||||||
}
|
|
||||||
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the server thread serving the client is in the DISCONNECTING state,
|
|
||||||
// disconnect from the pipe and reset the event. If anything fails, go into
|
|
||||||
// the ERROR state. If it goes well, go into the INITIAL state and set the
|
|
||||||
// event to start all over again.
|
|
||||||
void CrashGenerationServer::HandleDisconnectingState() {
|
|
||||||
assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
|
|
||||||
// Done serving the client.
|
|
||||||
client_info_ = NULL;
|
|
||||||
|
|
||||||
overlapped_.Internal = NULL;
|
|
||||||
overlapped_.InternalHigh = NULL;
|
|
||||||
overlapped_.Offset = 0;
|
|
||||||
overlapped_.OffsetHigh = 0;
|
|
||||||
overlapped_.Pointer = NULL;
|
|
||||||
|
|
||||||
if (!ResetEvent(overlapped_.hEvent)) {
|
|
||||||
EnterErrorState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DisconnectNamedPipe(pipe_)) {
|
|
||||||
EnterErrorState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the server is shutting down do not connect to the
|
|
||||||
// next client.
|
|
||||||
if (shutting_down_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_INITIAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrashGenerationServer::EnterErrorState() {
|
|
||||||
SetEvent(overlapped_.hEvent);
|
|
||||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) {
|
|
||||||
server_state_ = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrashGenerationServer::EnterStateImmediately(IPCServerState state) {
|
|
||||||
server_state_ = state;
|
|
||||||
|
|
||||||
if (!SetEvent(overlapped_.hEvent)) {
|
|
||||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info,
|
|
||||||
ProtocolMessage* reply) const {
|
|
||||||
reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE;
|
|
||||||
reply->id = GetCurrentProcessId();
|
|
||||||
|
|
||||||
if (CreateClientHandles(client_info, reply)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closing of remote handles (belonging to a different process) can
|
|
||||||
// only be done through DuplicateHandle.
|
|
||||||
if (reply->dump_request_handle) {
|
|
||||||
DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle
|
|
||||||
reply->dump_request_handle, // hSourceHandle
|
|
||||||
NULL, // hTargetProcessHandle
|
|
||||||
0, // lpTargetHandle
|
|
||||||
0, // dwDesiredAccess
|
|
||||||
FALSE, // bInheritHandle
|
|
||||||
DUPLICATE_CLOSE_SOURCE); // dwOptions
|
|
||||||
reply->dump_request_handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reply->dump_generated_handle) {
|
|
||||||
DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle
|
|
||||||
reply->dump_generated_handle, // hSourceHandle
|
|
||||||
NULL, // hTargetProcessHandle
|
|
||||||
0, // lpTargetHandle
|
|
||||||
0, // dwDesiredAccess
|
|
||||||
FALSE, // bInheritHandle
|
|
||||||
DUPLICATE_CLOSE_SOURCE); // dwOptions
|
|
||||||
reply->dump_generated_handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reply->server_alive_handle) {
|
|
||||||
DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle
|
|
||||||
reply->server_alive_handle, // hSourceHandle
|
|
||||||
NULL, // hTargetProcessHandle
|
|
||||||
0, // lpTargetHandle
|
|
||||||
0, // dwDesiredAccess
|
|
||||||
FALSE, // bInheritHandle
|
|
||||||
DUPLICATE_CLOSE_SOURCE); // dwOptions
|
|
||||||
reply->server_alive_handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info,
|
|
||||||
ProtocolMessage* reply) const {
|
|
||||||
HANDLE current_process = GetCurrentProcess();
|
|
||||||
if (!DuplicateHandle(current_process,
|
|
||||||
client_info.dump_requested_handle(),
|
|
||||||
client_info.process_handle(),
|
|
||||||
&reply->dump_request_handle,
|
|
||||||
kDumpRequestEventAccess,
|
|
||||||
FALSE,
|
|
||||||
0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DuplicateHandle(current_process,
|
|
||||||
client_info.dump_generated_handle(),
|
|
||||||
client_info.process_handle(),
|
|
||||||
&reply->dump_generated_handle,
|
|
||||||
kDumpGeneratedEventAccess,
|
|
||||||
FALSE,
|
|
||||||
0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DuplicateHandle(current_process,
|
|
||||||
server_alive_handle_,
|
|
||||||
client_info.process_handle(),
|
|
||||||
&reply->server_alive_handle,
|
|
||||||
kMutexAccess,
|
|
||||||
FALSE,
|
|
||||||
0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) {
|
|
||||||
ProtocolMessage reply;
|
|
||||||
if (!PrepareReply(*client_info, &reply)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD bytes_count = 0;
|
|
||||||
bool success = WriteFile(pipe_,
|
|
||||||
&reply,
|
|
||||||
sizeof(reply),
|
|
||||||
&bytes_count,
|
|
||||||
&overlapped_) != FALSE;
|
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
if (!success && error_code != ERROR_IO_PENDING) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes over ownership of client_info. We MUST return true if AddClient
|
|
||||||
// succeeds.
|
|
||||||
return AddClient(client_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The server thread servicing the clients runs this method. The method
|
|
||||||
// implements the state machine described in ReadMe.txt along with the
|
|
||||||
// helper methods HandleXXXState.
|
|
||||||
void CrashGenerationServer::HandleConnectionRequest() {
|
|
||||||
// If the server is shutting down, get into ERROR state, reset the event so
|
|
||||||
// more workers don't run and return immediately.
|
|
||||||
if (shutting_down_) {
|
|
||||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
|
||||||
ResetEvent(overlapped_.hEvent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (server_state_) {
|
|
||||||
case IPC_SERVER_STATE_ERROR:
|
|
||||||
HandleErrorState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_INITIAL:
|
|
||||||
HandleInitialState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_CONNECTING:
|
|
||||||
HandleConnectingState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_CONNECTED:
|
|
||||||
HandleConnectedState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_READING:
|
|
||||||
HandleReadingState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_READ_DONE:
|
|
||||||
HandleReadDoneState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_WRITING:
|
|
||||||
HandleWritingState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_WRITE_DONE:
|
|
||||||
HandleWriteDoneState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_READING_ACK:
|
|
||||||
HandleReadingAckState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPC_SERVER_STATE_DISCONNECTING:
|
|
||||||
HandleDisconnectingState();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
// This indicates that we added one more state without
|
|
||||||
// adding handling code.
|
|
||||||
server_state_ = IPC_SERVER_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationServer::AddClient(ClientInfo* client_info) {
|
|
||||||
HANDLE request_wait_handle = NULL;
|
|
||||||
if (!RegisterWaitForSingleObject(&request_wait_handle,
|
|
||||||
client_info->dump_requested_handle(),
|
|
||||||
OnDumpRequest,
|
|
||||||
client_info,
|
|
||||||
INFINITE,
|
|
||||||
kDumpRequestThreadFlags)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
client_info->set_dump_request_wait_handle(request_wait_handle);
|
|
||||||
|
|
||||||
// OnClientEnd will be called when the client process terminates.
|
|
||||||
HANDLE process_wait_handle = NULL;
|
|
||||||
if (!RegisterWaitForSingleObject(&process_wait_handle,
|
|
||||||
client_info->process_handle(),
|
|
||||||
OnClientEnd,
|
|
||||||
client_info,
|
|
||||||
INFINITE,
|
|
||||||
WT_EXECUTEONLYONCE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
client_info->set_process_exit_wait_handle(process_wait_handle);
|
|
||||||
|
|
||||||
// New scope to hold the lock for the shortest time.
|
|
||||||
{
|
|
||||||
AutoCriticalSection lock(&sync_);
|
|
||||||
if (shutting_down_) {
|
|
||||||
// If server is shutting down, don't add new clients
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
clients_.push_back(client_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
|
|
||||||
assert(context);
|
|
||||||
|
|
||||||
CrashGenerationServer* obj =
|
|
||||||
reinterpret_cast<CrashGenerationServer*>(context);
|
|
||||||
obj->HandleConnectionRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
|
|
||||||
assert(context);
|
|
||||||
ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
|
|
||||||
|
|
||||||
CrashGenerationServer* crash_server = client_info->crash_server();
|
|
||||||
assert(crash_server);
|
|
||||||
if (crash_server->pre_fetch_custom_info_) {
|
|
||||||
client_info->PopulateCustomInfo();
|
|
||||||
}
|
|
||||||
crash_server->HandleDumpRequest(*client_info);
|
|
||||||
|
|
||||||
ResetEvent(client_info->dump_requested_handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) {
|
|
||||||
assert(context);
|
|
||||||
ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
|
|
||||||
|
|
||||||
CrashGenerationServer* crash_server = client_info->crash_server();
|
|
||||||
assert(crash_server);
|
|
||||||
|
|
||||||
crash_server->HandleClientProcessExit(client_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrashGenerationServer::HandleClientProcessExit(ClientInfo* client_info) {
|
|
||||||
assert(client_info);
|
|
||||||
|
|
||||||
// Must unregister the dump request wait operation and wait for any
|
|
||||||
// dump requests that might be pending to finish before proceeding
|
|
||||||
// with the client_info cleanup.
|
|
||||||
client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending();
|
|
||||||
|
|
||||||
if (exit_callback_) {
|
|
||||||
exit_callback_(exit_context_, client_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a new scope to release lock automatically.
|
|
||||||
{
|
|
||||||
AutoCriticalSection lock(&sync_);
|
|
||||||
if (shutting_down_) {
|
|
||||||
// The crash generation server is shutting down and as part of the
|
|
||||||
// shutdown process it will delete all clients from the clients_ list.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
clients_.remove(client_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicitly unregister the process exit wait using the non-blocking method.
|
|
||||||
// Otherwise, the destructor will attempt to unregister it using the blocking
|
|
||||||
// method which will lead to a deadlock because it is being called from the
|
|
||||||
// callback of the same wait operation
|
|
||||||
client_info->UnregisterProcessExitWait(false);
|
|
||||||
|
|
||||||
delete client_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) {
|
|
||||||
bool execute_callback = true;
|
|
||||||
// Generate the dump only if it's explicitly requested by the
|
|
||||||
// server application; otherwise the server might want to generate
|
|
||||||
// dump in the callback.
|
|
||||||
std::wstring dump_path;
|
|
||||||
if (generate_dumps_) {
|
|
||||||
if (!GenerateDump(client_info, &dump_path)) {
|
|
||||||
// client proccess terminated or some other error
|
|
||||||
execute_callback = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump_callback_ && execute_callback) {
|
|
||||||
std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path;
|
|
||||||
dump_callback_(dump_context_, &client_info, ptr_dump_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetEvent(client_info.dump_generated_handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
|
|
||||||
std::wstring* dump_path) {
|
|
||||||
assert(client.pid() != 0);
|
|
||||||
assert(client.process_handle());
|
|
||||||
|
|
||||||
// We have to get the address of EXCEPTION_INFORMATION from
|
|
||||||
// the client process address space.
|
|
||||||
EXCEPTION_POINTERS* client_ex_info = NULL;
|
|
||||||
if (!client.GetClientExceptionInfo(&client_ex_info)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD client_thread_id = 0;
|
|
||||||
if (!client.GetClientThreadId(&client_thread_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpGenerator dump_generator(dump_path_,
|
|
||||||
client.process_handle(),
|
|
||||||
client.pid(),
|
|
||||||
client_thread_id,
|
|
||||||
GetCurrentThreadId(),
|
|
||||||
client_ex_info,
|
|
||||||
client.assert_info(),
|
|
||||||
client.dump_type(),
|
|
||||||
true);
|
|
||||||
if (!dump_generator.GenerateDumpFile(dump_path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return dump_generator.WriteMinidump();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,299 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__
|
|
||||||
#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include "client/windows/common/ipc_protocol.h"
|
|
||||||
#include "client/windows/crash_generation/minidump_generator.h"
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
class ClientInfo;
|
|
||||||
|
|
||||||
// Abstraction for server side implementation of out-of-process crash
|
|
||||||
// generation protocol for Windows platform only. It generates Windows
|
|
||||||
// minidump files for client processes that request dump generation. When
|
|
||||||
// the server is requested to start listening for clients (by calling the
|
|
||||||
// Start method), it creates a named pipe and waits for the clients to
|
|
||||||
// register. In response, it hands them event handles that the client can
|
|
||||||
// signal to request dump generation. When the clients request dump
|
|
||||||
// generation in this way, the server generates Windows minidump files.
|
|
||||||
class CrashGenerationServer {
|
|
||||||
public:
|
|
||||||
typedef void (*OnClientConnectedCallback)(void* context,
|
|
||||||
const ClientInfo* client_info);
|
|
||||||
|
|
||||||
typedef void (*OnClientDumpRequestCallback)(void* context,
|
|
||||||
const ClientInfo* client_info,
|
|
||||||
const std::wstring* file_path);
|
|
||||||
|
|
||||||
typedef void (*OnClientExitedCallback)(void* context,
|
|
||||||
const ClientInfo* client_info);
|
|
||||||
|
|
||||||
typedef void (*OnClientUploadRequestCallback)(void* context,
|
|
||||||
const DWORD crash_id);
|
|
||||||
|
|
||||||
// Creates an instance with the given parameters.
|
|
||||||
//
|
|
||||||
// Parameter pipe_name: Name of the Windows named pipe
|
|
||||||
// Parameter pipe_sec_attrs Security attributes to set on the pipe. Pass
|
|
||||||
// NULL to use default security on the pipe. By default, the pipe created
|
|
||||||
// allows Local System, Administrators and the Creator full control and
|
|
||||||
// the Everyone group read access on the pipe.
|
|
||||||
// Parameter connect_callback: Callback for a new client connection.
|
|
||||||
// Parameter connect_context: Context for client connection callback.
|
|
||||||
// Parameter crash_callback: Callback for a client crash dump request.
|
|
||||||
// Parameter crash_context: Context for client crash dump request callback.
|
|
||||||
// Parameter exit_callback: Callback for client process exit.
|
|
||||||
// Parameter exit_context: Context for client exit callback.
|
|
||||||
// Parameter generate_dumps: Whether to automatically generate dumps.
|
|
||||||
// Client code of this class might want to generate dumps explicitly in the
|
|
||||||
// crash dump request callback. In that case, false can be passed for this
|
|
||||||
// parameter.
|
|
||||||
// Parameter dump_path: Path for generating dumps; required only if true is
|
|
||||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
|
||||||
CrashGenerationServer(const std::wstring& pipe_name,
|
|
||||||
SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
|
||||||
OnClientConnectedCallback connect_callback,
|
|
||||||
void* connect_context,
|
|
||||||
OnClientDumpRequestCallback dump_callback,
|
|
||||||
void* dump_context,
|
|
||||||
OnClientExitedCallback exit_callback,
|
|
||||||
void* exit_context,
|
|
||||||
OnClientUploadRequestCallback upload_request_callback,
|
|
||||||
void* upload_context,
|
|
||||||
bool generate_dumps,
|
|
||||||
const std::wstring* dump_path);
|
|
||||||
|
|
||||||
~CrashGenerationServer();
|
|
||||||
|
|
||||||
// Performs initialization steps needed to start listening to clients. Upon
|
|
||||||
// successful return clients may connect to this server's pipe.
|
|
||||||
//
|
|
||||||
// Returns true if initialization is successful; false otherwise.
|
|
||||||
bool Start();
|
|
||||||
|
|
||||||
void pre_fetch_custom_info(bool do_pre_fetch) {
|
|
||||||
pre_fetch_custom_info_ = do_pre_fetch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Various states the client can be in during the handshake with
|
|
||||||
// the server.
|
|
||||||
enum IPCServerState {
|
|
||||||
// Server starts in this state.
|
|
||||||
IPC_SERVER_STATE_UNINITIALIZED,
|
|
||||||
|
|
||||||
// Server is in error state and it cannot serve any clients.
|
|
||||||
IPC_SERVER_STATE_ERROR,
|
|
||||||
|
|
||||||
// Server starts in this state.
|
|
||||||
IPC_SERVER_STATE_INITIAL,
|
|
||||||
|
|
||||||
// Server has issued an async connect to the pipe and it is waiting
|
|
||||||
// for the connection to be established.
|
|
||||||
IPC_SERVER_STATE_CONNECTING,
|
|
||||||
|
|
||||||
// Server is connected successfully.
|
|
||||||
IPC_SERVER_STATE_CONNECTED,
|
|
||||||
|
|
||||||
// Server has issued an async read from the pipe and it is waiting for
|
|
||||||
// the read to finish.
|
|
||||||
IPC_SERVER_STATE_READING,
|
|
||||||
|
|
||||||
// Server is done reading from the pipe.
|
|
||||||
IPC_SERVER_STATE_READ_DONE,
|
|
||||||
|
|
||||||
// Server has issued an async write to the pipe and it is waiting for
|
|
||||||
// the write to finish.
|
|
||||||
IPC_SERVER_STATE_WRITING,
|
|
||||||
|
|
||||||
// Server is done writing to the pipe.
|
|
||||||
IPC_SERVER_STATE_WRITE_DONE,
|
|
||||||
|
|
||||||
// Server has issued an async read from the pipe for an ack and it
|
|
||||||
// is waiting for the read to finish.
|
|
||||||
IPC_SERVER_STATE_READING_ACK,
|
|
||||||
|
|
||||||
// Server is done writing to the pipe and it is now ready to disconnect
|
|
||||||
// and reconnect.
|
|
||||||
IPC_SERVER_STATE_DISCONNECTING
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Helper methods to handle various server IPC states.
|
|
||||||
//
|
|
||||||
void HandleErrorState();
|
|
||||||
void HandleInitialState();
|
|
||||||
void HandleConnectingState();
|
|
||||||
void HandleConnectedState();
|
|
||||||
void HandleReadingState();
|
|
||||||
void HandleReadDoneState();
|
|
||||||
void HandleWritingState();
|
|
||||||
void HandleWriteDoneState();
|
|
||||||
void HandleReadingAckState();
|
|
||||||
void HandleDisconnectingState();
|
|
||||||
|
|
||||||
// Prepares reply for a client from the given parameters.
|
|
||||||
bool PrepareReply(const ClientInfo& client_info,
|
|
||||||
ProtocolMessage* reply) const;
|
|
||||||
|
|
||||||
// Duplicates various handles in the ClientInfo object for the client
|
|
||||||
// process and stores them in the given ProtocolMessage instance. If
|
|
||||||
// creating any handle fails, ProtocolMessage will contain the handles
|
|
||||||
// already created successfully, which should be closed by the caller.
|
|
||||||
bool CreateClientHandles(const ClientInfo& client_info,
|
|
||||||
ProtocolMessage* reply) const;
|
|
||||||
|
|
||||||
// Response to the given client. Return true if all steps of
|
|
||||||
// responding to the client succeed, false otherwise.
|
|
||||||
bool RespondToClient(ClientInfo* client_info);
|
|
||||||
|
|
||||||
// Handles a connection request from the client.
|
|
||||||
void HandleConnectionRequest();
|
|
||||||
|
|
||||||
// Handles a dump request from the client.
|
|
||||||
void HandleDumpRequest(const ClientInfo& client_info);
|
|
||||||
|
|
||||||
// Callback for pipe connected event.
|
|
||||||
static void CALLBACK OnPipeConnected(void* context, BOOLEAN timer_or_wait);
|
|
||||||
|
|
||||||
// Callback for a dump request.
|
|
||||||
static void CALLBACK OnDumpRequest(void* context, BOOLEAN timer_or_wait);
|
|
||||||
|
|
||||||
// Callback for client process exit event.
|
|
||||||
static void CALLBACK OnClientEnd(void* context, BOOLEAN timer_or_wait);
|
|
||||||
|
|
||||||
// Handles client process exit.
|
|
||||||
void HandleClientProcessExit(ClientInfo* client_info);
|
|
||||||
|
|
||||||
// Adds the given client to the list of registered clients.
|
|
||||||
bool AddClient(ClientInfo* client_info);
|
|
||||||
|
|
||||||
// Generates dump for the given client.
|
|
||||||
bool GenerateDump(const ClientInfo& client, std::wstring* dump_path);
|
|
||||||
|
|
||||||
// Puts the server in a permanent error state and sets a signal such that
|
|
||||||
// the state will be immediately entered after the current state transition
|
|
||||||
// is complete.
|
|
||||||
void EnterErrorState();
|
|
||||||
|
|
||||||
// Puts the server in the specified state and sets a signal such that the
|
|
||||||
// state is immediately entered after the current state transition is
|
|
||||||
// complete.
|
|
||||||
void EnterStateImmediately(IPCServerState state);
|
|
||||||
|
|
||||||
// Puts the server in the specified state. No signal will be set, so the state
|
|
||||||
// transition will only occur when signaled manually or by completion of an
|
|
||||||
// asynchronous IO operation.
|
|
||||||
void EnterStateWhenSignaled(IPCServerState state);
|
|
||||||
|
|
||||||
// Sync object for thread-safe access to the shared list of clients.
|
|
||||||
CRITICAL_SECTION sync_;
|
|
||||||
|
|
||||||
// List of clients.
|
|
||||||
std::list<ClientInfo*> clients_;
|
|
||||||
|
|
||||||
// Pipe name.
|
|
||||||
std::wstring pipe_name_;
|
|
||||||
|
|
||||||
// Pipe security attributes
|
|
||||||
SECURITY_ATTRIBUTES* pipe_sec_attrs_;
|
|
||||||
|
|
||||||
// Handle to the pipe used for handshake with clients.
|
|
||||||
HANDLE pipe_;
|
|
||||||
|
|
||||||
// Pipe wait handle.
|
|
||||||
HANDLE pipe_wait_handle_;
|
|
||||||
|
|
||||||
// Handle to server-alive mutex.
|
|
||||||
HANDLE server_alive_handle_;
|
|
||||||
|
|
||||||
// Callback for a successful client connection.
|
|
||||||
OnClientConnectedCallback connect_callback_;
|
|
||||||
|
|
||||||
// Context for client connected callback.
|
|
||||||
void* connect_context_;
|
|
||||||
|
|
||||||
// Callback for a client dump request.
|
|
||||||
OnClientDumpRequestCallback dump_callback_;
|
|
||||||
|
|
||||||
// Context for client dump request callback.
|
|
||||||
void* dump_context_;
|
|
||||||
|
|
||||||
// Callback for client process exit.
|
|
||||||
OnClientExitedCallback exit_callback_;
|
|
||||||
|
|
||||||
// Context for client process exit callback.
|
|
||||||
void* exit_context_;
|
|
||||||
|
|
||||||
// Callback for upload request.
|
|
||||||
OnClientUploadRequestCallback upload_request_callback_;
|
|
||||||
|
|
||||||
// Context for upload request callback.
|
|
||||||
void* upload_context_;
|
|
||||||
|
|
||||||
// Whether to generate dumps.
|
|
||||||
bool generate_dumps_;
|
|
||||||
|
|
||||||
// Wether to populate custom information up-front.
|
|
||||||
bool pre_fetch_custom_info_;
|
|
||||||
|
|
||||||
// The dump path for the server.
|
|
||||||
const std::wstring dump_path_;
|
|
||||||
|
|
||||||
// State of the server in performing the IPC with the client.
|
|
||||||
// Note that since we restrict the pipe to one instance, we
|
|
||||||
// only need to keep one state of the server. Otherwise, server
|
|
||||||
// would have one state per client it is talking to.
|
|
||||||
IPCServerState server_state_;
|
|
||||||
|
|
||||||
// Whether the server is shutting down.
|
|
||||||
bool shutting_down_;
|
|
||||||
|
|
||||||
// Overlapped instance for async I/O on the pipe.
|
|
||||||
OVERLAPPED overlapped_;
|
|
||||||
|
|
||||||
// Message object used in IPC with the client.
|
|
||||||
ProtocolMessage msg_;
|
|
||||||
|
|
||||||
// Client Info for the client that's connecting to the server.
|
|
||||||
ClientInfo* client_info_;
|
|
||||||
|
|
||||||
// Disable copy ctor and operator=.
|
|
||||||
CrashGenerationServer(const CrashGenerationServer& crash_server);
|
|
||||||
CrashGenerationServer& operator=(const CrashGenerationServer& crash_server);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__
|
|
@ -1,579 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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/windows/crash_generation/minidump_generator.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <avrfsdk.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "client/windows/common/auto_critical_section.h"
|
|
||||||
#include "common/scoped_ptr.h"
|
|
||||||
#include "common/windows/guid_string.h"
|
|
||||||
|
|
||||||
using std::wstring;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// A helper class used to collect handle operations data. Unlike
|
|
||||||
// |MiniDumpWithHandleData| it records the operations for a single handle value
|
|
||||||
// only, making it possible to include this information to a minidump.
|
|
||||||
class HandleTraceData {
|
|
||||||
public:
|
|
||||||
HandleTraceData();
|
|
||||||
~HandleTraceData();
|
|
||||||
|
|
||||||
// Collects the handle operations data and formats a user stream to be added
|
|
||||||
// to the minidump.
|
|
||||||
bool CollectHandleData(HANDLE process_handle,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers);
|
|
||||||
|
|
||||||
// Fills the user dump entry with a pointer to the collected handle operations
|
|
||||||
// data. Returns |true| if the entry was initialized successfully, or |false|
|
|
||||||
// if no trace data is available.
|
|
||||||
bool GetUserStream(MINIDUMP_USER_STREAM* user_stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Reads the exception code from the client process's address space.
|
|
||||||
// This routine assumes that the client process's pointer width matches ours.
|
|
||||||
static bool ReadExceptionCode(HANDLE process_handle,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
DWORD* exception_code);
|
|
||||||
|
|
||||||
// Stores handle operations retrieved by VerifierEnumerateResource().
|
|
||||||
static ULONG CALLBACK RecordHandleOperations(void* resource_description,
|
|
||||||
void* enumeration_context,
|
|
||||||
ULONG* enumeration_level);
|
|
||||||
|
|
||||||
// Function pointer type for VerifierEnumerateResource, which is looked up
|
|
||||||
// dynamically.
|
|
||||||
typedef BOOL (WINAPI* VerifierEnumerateResourceType)(
|
|
||||||
HANDLE Process,
|
|
||||||
ULONG Flags,
|
|
||||||
ULONG ResourceType,
|
|
||||||
AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback,
|
|
||||||
PVOID EnumerationContext);
|
|
||||||
|
|
||||||
// Handle to dynamically loaded verifier.dll.
|
|
||||||
HMODULE verifier_module_;
|
|
||||||
|
|
||||||
// Pointer to the VerifierEnumerateResource function.
|
|
||||||
VerifierEnumerateResourceType enumerate_resource_;
|
|
||||||
|
|
||||||
// Handle value to look for.
|
|
||||||
ULONG64 handle_;
|
|
||||||
|
|
||||||
// List of handle operations for |handle_|.
|
|
||||||
std::list<AVRF_HANDLE_OPERATION> operations_;
|
|
||||||
|
|
||||||
// Minidump stream data.
|
|
||||||
std::vector<char> stream_;
|
|
||||||
};
|
|
||||||
|
|
||||||
HandleTraceData::HandleTraceData()
|
|
||||||
: verifier_module_(NULL),
|
|
||||||
enumerate_resource_(NULL),
|
|
||||||
handle_(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
HandleTraceData::~HandleTraceData() {
|
|
||||||
if (verifier_module_) {
|
|
||||||
FreeLibrary(verifier_module_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleTraceData::CollectHandleData(
|
|
||||||
HANDLE process_handle,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers) {
|
|
||||||
DWORD exception_code;
|
|
||||||
if (!ReadExceptionCode(process_handle, exception_pointers, &exception_code)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify whether the execption is STATUS_INVALID_HANDLE. Do not record any
|
|
||||||
// handle information if it is a different exception to keep the minidump
|
|
||||||
// small.
|
|
||||||
if (exception_code != STATUS_INVALID_HANDLE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load verifier!VerifierEnumerateResource() dynamically.
|
|
||||||
verifier_module_ = LoadLibrary(TEXT("verifier.dll"));
|
|
||||||
if (!verifier_module_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerate_resource_ = reinterpret_cast<VerifierEnumerateResourceType>(
|
|
||||||
GetProcAddress(verifier_module_, "VerifierEnumerateResource"));
|
|
||||||
if (!enumerate_resource_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// STATUS_INVALID_HANDLE does not provide the offending handle value in
|
|
||||||
// the exception parameters so we have to guess. At the moment we scan
|
|
||||||
// the handle operations trace looking for the last invalid handle operation
|
|
||||||
// and record only the operations for that handle value.
|
|
||||||
if (enumerate_resource_(process_handle,
|
|
||||||
0,
|
|
||||||
AvrfResourceHandleTrace,
|
|
||||||
&RecordHandleOperations,
|
|
||||||
this) != ERROR_SUCCESS) {
|
|
||||||
// The handle tracing must have not been enabled.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that |handle_| is initialized, purge all irrelevant operations.
|
|
||||||
std::list<AVRF_HANDLE_OPERATION>::iterator i = operations_.begin();
|
|
||||||
std::list<AVRF_HANDLE_OPERATION>::iterator i_end = operations_.end();
|
|
||||||
while (i != i_end) {
|
|
||||||
if (i->Handle == handle_) {
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
i = operations_.erase(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the list of recorded operations to a minidump stream.
|
|
||||||
stream_.resize(sizeof(MINIDUMP_HANDLE_OPERATION_LIST) +
|
|
||||||
sizeof(AVRF_HANDLE_OPERATION) * operations_.size());
|
|
||||||
|
|
||||||
MINIDUMP_HANDLE_OPERATION_LIST* stream_data =
|
|
||||||
reinterpret_cast<MINIDUMP_HANDLE_OPERATION_LIST*>(
|
|
||||||
&stream_.front());
|
|
||||||
stream_data->SizeOfHeader = sizeof(MINIDUMP_HANDLE_OPERATION_LIST);
|
|
||||||
stream_data->SizeOfEntry = sizeof(AVRF_HANDLE_OPERATION);
|
|
||||||
stream_data->NumberOfEntries = static_cast<ULONG32>(operations_.size());
|
|
||||||
stream_data->Reserved = 0;
|
|
||||||
std::copy(operations_.begin(),
|
|
||||||
operations_.end(),
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
|
|
||||||
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
|
|
||||||
operations_.size())
|
|
||||||
#else
|
|
||||||
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1)
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleTraceData::GetUserStream(MINIDUMP_USER_STREAM* user_stream) {
|
|
||||||
if (stream_.empty()) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
user_stream->Type = HandleOperationListStream;
|
|
||||||
user_stream->BufferSize = static_cast<ULONG>(stream_.size());
|
|
||||||
user_stream->Buffer = &stream_.front();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleTraceData::ReadExceptionCode(
|
|
||||||
HANDLE process_handle,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
DWORD* exception_code) {
|
|
||||||
EXCEPTION_POINTERS pointers;
|
|
||||||
if (!ReadProcessMemory(process_handle,
|
|
||||||
exception_pointers,
|
|
||||||
&pointers,
|
|
||||||
sizeof(pointers),
|
|
||||||
NULL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ReadProcessMemory(process_handle,
|
|
||||||
pointers.ExceptionRecord,
|
|
||||||
exception_code,
|
|
||||||
sizeof(*exception_code),
|
|
||||||
NULL)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG CALLBACK HandleTraceData::RecordHandleOperations(
|
|
||||||
void* resource_description,
|
|
||||||
void* enumeration_context,
|
|
||||||
ULONG* enumeration_level) {
|
|
||||||
AVRF_HANDLE_OPERATION* description =
|
|
||||||
reinterpret_cast<AVRF_HANDLE_OPERATION*>(resource_description);
|
|
||||||
HandleTraceData* self =
|
|
||||||
reinterpret_cast<HandleTraceData*>(enumeration_context);
|
|
||||||
|
|
||||||
// Remember the last invalid handle operation.
|
|
||||||
if (description->OperationType == OperationDbBADREF) {
|
|
||||||
self->handle_ = description->Handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record all handle operations.
|
|
||||||
self->operations_.push_back(*description);
|
|
||||||
|
|
||||||
*enumeration_level = HeapEnumerationEverything;
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
MinidumpGenerator::MinidumpGenerator(
|
|
||||||
const std::wstring& dump_path,
|
|
||||||
const HANDLE process_handle,
|
|
||||||
const DWORD process_id,
|
|
||||||
const DWORD thread_id,
|
|
||||||
const DWORD requesting_thread_id,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
const MINIDUMP_TYPE dump_type,
|
|
||||||
const bool is_client_pointers)
|
|
||||||
: dbghelp_module_(NULL),
|
|
||||||
rpcrt4_module_(NULL),
|
|
||||||
dump_path_(dump_path),
|
|
||||||
process_handle_(process_handle),
|
|
||||||
process_id_(process_id),
|
|
||||||
thread_id_(thread_id),
|
|
||||||
requesting_thread_id_(requesting_thread_id),
|
|
||||||
exception_pointers_(exception_pointers),
|
|
||||||
assert_info_(assert_info),
|
|
||||||
dump_type_(dump_type),
|
|
||||||
is_client_pointers_(is_client_pointers),
|
|
||||||
dump_file_(INVALID_HANDLE_VALUE),
|
|
||||||
full_dump_file_(INVALID_HANDLE_VALUE),
|
|
||||||
dump_file_is_internal_(false),
|
|
||||||
full_dump_file_is_internal_(false),
|
|
||||||
additional_streams_(NULL),
|
|
||||||
callback_info_(NULL),
|
|
||||||
write_dump_(NULL),
|
|
||||||
create_uuid_(NULL) {
|
|
||||||
InitializeCriticalSection(&module_load_sync_);
|
|
||||||
InitializeCriticalSection(&get_proc_address_sync_);
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpGenerator::~MinidumpGenerator() {
|
|
||||||
if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(dump_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(full_dump_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dbghelp_module_) {
|
|
||||||
FreeLibrary(dbghelp_module_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rpcrt4_module_) {
|
|
||||||
FreeLibrary(rpcrt4_module_);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteCriticalSection(&get_proc_address_sync_);
|
|
||||||
DeleteCriticalSection(&module_load_sync_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpGenerator::WriteMinidump() {
|
|
||||||
bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0;
|
|
||||||
if (dump_file_ == INVALID_HANDLE_VALUE ||
|
|
||||||
(full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MiniDumpWriteDumpType write_dump = GetWriteDump();
|
|
||||||
if (!write_dump) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
|
|
||||||
MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
|
|
||||||
|
|
||||||
// Setup the exception information object only if it's a dump
|
|
||||||
// due to an exception.
|
|
||||||
if (exception_pointers_) {
|
|
||||||
dump_exception_pointers = &dump_exception_info;
|
|
||||||
dump_exception_info.ThreadId = thread_id_;
|
|
||||||
dump_exception_info.ExceptionPointers = exception_pointers_;
|
|
||||||
dump_exception_info.ClientPointers = is_client_pointers_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an MDRawBreakpadInfo stream to the minidump, to provide additional
|
|
||||||
// information about the exception handler to the Breakpad processor.
|
|
||||||
// The information will help the processor determine which threads are
|
|
||||||
// relevant. The Breakpad processor does not require this information but
|
|
||||||
// can function better with Breakpad-generated dumps when it is present.
|
|
||||||
// The native debugger is not harmed by the presence of this information.
|
|
||||||
MDRawBreakpadInfo breakpad_info = {0};
|
|
||||||
if (!is_client_pointers_) {
|
|
||||||
// Set the dump thread id and requesting thread id only in case of
|
|
||||||
// in-process dump generation.
|
|
||||||
breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
|
||||||
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
|
||||||
breakpad_info.dump_thread_id = thread_id_;
|
|
||||||
breakpad_info.requesting_thread_id = requesting_thread_id_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int additional_streams_count = additional_streams_ ?
|
|
||||||
additional_streams_->UserStreamCount : 0;
|
|
||||||
scoped_array<MINIDUMP_USER_STREAM> user_stream_array(
|
|
||||||
new MINIDUMP_USER_STREAM[3 + additional_streams_count]);
|
|
||||||
user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
|
|
||||||
user_stream_array[0].BufferSize = sizeof(breakpad_info);
|
|
||||||
user_stream_array[0].Buffer = &breakpad_info;
|
|
||||||
|
|
||||||
MINIDUMP_USER_STREAM_INFORMATION user_streams;
|
|
||||||
user_streams.UserStreamCount = 1;
|
|
||||||
user_streams.UserStreamArray = user_stream_array.get();
|
|
||||||
|
|
||||||
MDRawAssertionInfo* actual_assert_info = assert_info_;
|
|
||||||
MDRawAssertionInfo client_assert_info = {{0}};
|
|
||||||
|
|
||||||
if (assert_info_) {
|
|
||||||
// If the assertion info object lives in the client process,
|
|
||||||
// read the memory of the client process.
|
|
||||||
if (is_client_pointers_) {
|
|
||||||
SIZE_T bytes_read = 0;
|
|
||||||
if (!ReadProcessMemory(process_handle_,
|
|
||||||
assert_info_,
|
|
||||||
&client_assert_info,
|
|
||||||
sizeof(client_assert_info),
|
|
||||||
&bytes_read)) {
|
|
||||||
if (dump_file_is_internal_)
|
|
||||||
CloseHandle(dump_file_);
|
|
||||||
if (full_dump_file_is_internal_ &&
|
|
||||||
full_dump_file_ != INVALID_HANDLE_VALUE)
|
|
||||||
CloseHandle(full_dump_file_);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes_read != sizeof(client_assert_info)) {
|
|
||||||
if (dump_file_is_internal_)
|
|
||||||
CloseHandle(dump_file_);
|
|
||||||
if (full_dump_file_is_internal_ &&
|
|
||||||
full_dump_file_ != INVALID_HANDLE_VALUE)
|
|
||||||
CloseHandle(full_dump_file_);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
actual_assert_info = &client_assert_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
|
|
||||||
user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
|
|
||||||
user_stream_array[1].Buffer = actual_assert_info;
|
|
||||||
++user_streams.UserStreamCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (additional_streams_) {
|
|
||||||
for (size_t i = 0;
|
|
||||||
i < additional_streams_->UserStreamCount;
|
|
||||||
i++, user_streams.UserStreamCount++) {
|
|
||||||
user_stream_array[user_streams.UserStreamCount].Type =
|
|
||||||
additional_streams_->UserStreamArray[i].Type;
|
|
||||||
user_stream_array[user_streams.UserStreamCount].BufferSize =
|
|
||||||
additional_streams_->UserStreamArray[i].BufferSize;
|
|
||||||
user_stream_array[user_streams.UserStreamCount].Buffer =
|
|
||||||
additional_streams_->UserStreamArray[i].Buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the process is terminated by STATUS_INVALID_HANDLE exception store
|
|
||||||
// the trace of operations for the offending handle value. Do nothing special
|
|
||||||
// if the client already requested the handle trace to be stored in the dump.
|
|
||||||
HandleTraceData handle_trace_data;
|
|
||||||
if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) {
|
|
||||||
if (!handle_trace_data.CollectHandleData(process_handle_,
|
|
||||||
exception_pointers_)) {
|
|
||||||
if (dump_file_is_internal_)
|
|
||||||
CloseHandle(dump_file_);
|
|
||||||
if (full_dump_file_is_internal_ &&
|
|
||||||
full_dump_file_ != INVALID_HANDLE_VALUE)
|
|
||||||
CloseHandle(full_dump_file_);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result_full_memory = true;
|
|
||||||
if (full_memory_dump) {
|
|
||||||
result_full_memory = write_dump(
|
|
||||||
process_handle_,
|
|
||||||
process_id_,
|
|
||||||
full_dump_file_,
|
|
||||||
static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal))
|
|
||||||
| MiniDumpWithHandleData),
|
|
||||||
exception_pointers_ ? &dump_exception_info : NULL,
|
|
||||||
&user_streams,
|
|
||||||
NULL) != FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add handle operations trace stream to the minidump if it was collected.
|
|
||||||
if (handle_trace_data.GetUserStream(
|
|
||||||
&user_stream_array[user_streams.UserStreamCount])) {
|
|
||||||
++user_streams.UserStreamCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result_minidump = write_dump(
|
|
||||||
process_handle_,
|
|
||||||
process_id_,
|
|
||||||
dump_file_,
|
|
||||||
static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory))
|
|
||||||
| MiniDumpNormal),
|
|
||||||
exception_pointers_ ? &dump_exception_info : NULL,
|
|
||||||
&user_streams,
|
|
||||||
callback_info_) != FALSE;
|
|
||||||
|
|
||||||
return result_minidump && result_full_memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) {
|
|
||||||
// The dump file was already set by handle or this function was previously
|
|
||||||
// called.
|
|
||||||
if (dump_file_ != INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wstring dump_file_path;
|
|
||||||
if (!GenerateDumpFilePath(&dump_file_path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dump_file_ = CreateFile(dump_file_path.c_str(),
|
|
||||||
GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
CREATE_NEW,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
if (dump_file_ == INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dump_file_is_internal_ = true;
|
|
||||||
*dump_path = dump_file_path;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) {
|
|
||||||
// A full minidump was not requested.
|
|
||||||
if ((dump_type_ & MiniDumpWithFullMemory) == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The dump file was already set by handle or this function was previously
|
|
||||||
// called.
|
|
||||||
if (full_dump_file_ != INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wstring full_dump_file_path;
|
|
||||||
if (!GenerateDumpFilePath(&full_dump_file_path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
|
|
||||||
full_dump_file_path.append(TEXT("-full.dmp"));
|
|
||||||
|
|
||||||
full_dump_file_ = CreateFile(full_dump_file_path.c_str(),
|
|
||||||
GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
CREATE_NEW,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
if (full_dump_file_ == INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
full_dump_file_is_internal_ = true;
|
|
||||||
*full_dump_path = full_dump_file_path;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
HMODULE MinidumpGenerator::GetDbghelpModule() {
|
|
||||||
AutoCriticalSection lock(&module_load_sync_);
|
|
||||||
if (!dbghelp_module_) {
|
|
||||||
dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dbghelp_module_;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() {
|
|
||||||
AutoCriticalSection lock(&get_proc_address_sync_);
|
|
||||||
if (!write_dump_) {
|
|
||||||
HMODULE module = GetDbghelpModule();
|
|
||||||
if (module) {
|
|
||||||
FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump");
|
|
||||||
write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return write_dump_;
|
|
||||||
}
|
|
||||||
|
|
||||||
HMODULE MinidumpGenerator::GetRpcrt4Module() {
|
|
||||||
AutoCriticalSection lock(&module_load_sync_);
|
|
||||||
if (!rpcrt4_module_) {
|
|
||||||
rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rpcrt4_module_;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
|
|
||||||
AutoCriticalSection lock(&module_load_sync_);
|
|
||||||
if (!create_uuid_) {
|
|
||||||
HMODULE module = GetRpcrt4Module();
|
|
||||||
if (module) {
|
|
||||||
FARPROC proc = GetProcAddress(module, "UuidCreate");
|
|
||||||
create_uuid_ = reinterpret_cast<UuidCreateType>(proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_uuid_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
|
|
||||||
UUID id = {0};
|
|
||||||
|
|
||||||
UuidCreateType create_uuid = GetCreateUuid();
|
|
||||||
if (!create_uuid) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
create_uuid(&id);
|
|
||||||
wstring id_str = GUIDString::GUIDToWString(&id);
|
|
||||||
|
|
||||||
*file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,199 +0,0 @@
|
|||||||
// Copyright (c) 2008, 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_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
|
|
||||||
#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
#include <rpc.h>
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
|
||||||
|
|
||||||
// Abstraction for various objects and operations needed to generate
|
|
||||||
// minidump on Windows. This abstraction is useful to hide all the gory
|
|
||||||
// details for minidump generation and provide a clean interface to
|
|
||||||
// the clients to generate minidumps.
|
|
||||||
class MinidumpGenerator {
|
|
||||||
public:
|
|
||||||
// Creates an instance with the given parameters.
|
|
||||||
// is_client_pointers specifies whether the exception_pointers and
|
|
||||||
// assert_info point into the process that is being dumped.
|
|
||||||
// Before calling WriteMinidump on the returned instance a dump file muct be
|
|
||||||
// specified by a call to either SetDumpFile() or GenerateDumpFile().
|
|
||||||
// If a full dump file will be requested via a subsequent call to either
|
|
||||||
// SetFullDumpFile or GenerateFullDumpFile() dump_type must include
|
|
||||||
// MiniDumpWithFullMemory.
|
|
||||||
MinidumpGenerator(const std::wstring& dump_path,
|
|
||||||
const HANDLE process_handle,
|
|
||||||
const DWORD process_id,
|
|
||||||
const DWORD thread_id,
|
|
||||||
const DWORD requesting_thread_id,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
const MINIDUMP_TYPE dump_type,
|
|
||||||
const bool is_client_pointers);
|
|
||||||
|
|
||||||
~MinidumpGenerator();
|
|
||||||
|
|
||||||
void SetDumpFile(const HANDLE dump_file) { dump_file_ = dump_file; }
|
|
||||||
void SetFullDumpFile(const HANDLE full_dump_file) {
|
|
||||||
full_dump_file_ = full_dump_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the name for the dump file that will be written to once
|
|
||||||
// WriteMinidump() is called. Can only be called once and cannot be called
|
|
||||||
// if the dump file is set via SetDumpFile().
|
|
||||||
bool GenerateDumpFile(std::wstring* dump_path);
|
|
||||||
|
|
||||||
// Generate the name for the full dump file that will be written to once
|
|
||||||
// WriteMinidump() is called. Cannot be called unless the minidump type
|
|
||||||
// includes MiniDumpWithFullMemory. Can only be called once and cannot be
|
|
||||||
// called if the dump file is set via SetFullDumpFile().
|
|
||||||
bool GenerateFullDumpFile(std::wstring* full_dump_path);
|
|
||||||
|
|
||||||
void SetAdditionalStreams(
|
|
||||||
MINIDUMP_USER_STREAM_INFORMATION* additional_streams) {
|
|
||||||
additional_streams_ = additional_streams;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetCallback(MINIDUMP_CALLBACK_INFORMATION* callback_info) {
|
|
||||||
callback_info_ = callback_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes the minidump with the given parameters. Stores the
|
|
||||||
// dump file path in the dump_path parameter if dump generation
|
|
||||||
// succeeds.
|
|
||||||
bool WriteMinidump();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Function pointer type for MiniDumpWriteDump, which is looked up
|
|
||||||
// dynamically.
|
|
||||||
typedef BOOL (WINAPI* MiniDumpWriteDumpType)(
|
|
||||||
HANDLE hProcess,
|
|
||||||
DWORD ProcessId,
|
|
||||||
HANDLE hFile,
|
|
||||||
MINIDUMP_TYPE DumpType,
|
|
||||||
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
||||||
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
||||||
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
|
||||||
|
|
||||||
// Function pointer type for UuidCreate, which is looked up dynamically.
|
|
||||||
typedef RPC_STATUS (RPC_ENTRY* UuidCreateType)(UUID* Uuid);
|
|
||||||
|
|
||||||
// Loads the appropriate DLL lazily in a thread safe way.
|
|
||||||
HMODULE GetDbghelpModule();
|
|
||||||
|
|
||||||
// Loads the appropriate DLL and gets a pointer to the MiniDumpWriteDump
|
|
||||||
// function lazily and in a thread-safe manner.
|
|
||||||
MiniDumpWriteDumpType GetWriteDump();
|
|
||||||
|
|
||||||
// Loads the appropriate DLL lazily in a thread safe way.
|
|
||||||
HMODULE GetRpcrt4Module();
|
|
||||||
|
|
||||||
// Loads the appropriate DLL and gets a pointer to the UuidCreate
|
|
||||||
// function lazily and in a thread-safe manner.
|
|
||||||
UuidCreateType GetCreateUuid();
|
|
||||||
|
|
||||||
// Returns the path for the file to write dump to.
|
|
||||||
bool GenerateDumpFilePath(std::wstring* file_path);
|
|
||||||
|
|
||||||
// Handle to dynamically loaded DbgHelp.dll.
|
|
||||||
HMODULE dbghelp_module_;
|
|
||||||
|
|
||||||
// Pointer to the MiniDumpWriteDump function.
|
|
||||||
MiniDumpWriteDumpType write_dump_;
|
|
||||||
|
|
||||||
// Handle to dynamically loaded rpcrt4.dll.
|
|
||||||
HMODULE rpcrt4_module_;
|
|
||||||
|
|
||||||
// Pointer to the UuidCreate function.
|
|
||||||
UuidCreateType create_uuid_;
|
|
||||||
|
|
||||||
// Handle for the process to dump.
|
|
||||||
HANDLE process_handle_;
|
|
||||||
|
|
||||||
// Process ID for the process to dump.
|
|
||||||
DWORD process_id_;
|
|
||||||
|
|
||||||
// The crashing thread ID.
|
|
||||||
DWORD thread_id_;
|
|
||||||
|
|
||||||
// The thread ID which is requesting the dump.
|
|
||||||
DWORD requesting_thread_id_;
|
|
||||||
|
|
||||||
// Pointer to the exception information for the crash. This may point to an
|
|
||||||
// address in the crashing process so it should not be dereferenced.
|
|
||||||
EXCEPTION_POINTERS* exception_pointers_;
|
|
||||||
|
|
||||||
// Assertion info for the report.
|
|
||||||
MDRawAssertionInfo* assert_info_;
|
|
||||||
|
|
||||||
// Type of minidump to generate.
|
|
||||||
MINIDUMP_TYPE dump_type_;
|
|
||||||
|
|
||||||
// Specifies whether the exception_pointers_ reference memory in the crashing
|
|
||||||
// process.
|
|
||||||
bool is_client_pointers_;
|
|
||||||
|
|
||||||
// Folder path to store dump files.
|
|
||||||
std::wstring dump_path_;
|
|
||||||
|
|
||||||
// The file where the dump will be written.
|
|
||||||
HANDLE dump_file_;
|
|
||||||
|
|
||||||
// The file where the full dump will be written.
|
|
||||||
HANDLE full_dump_file_;
|
|
||||||
|
|
||||||
// Tracks whether the dump file handle is managed externally.
|
|
||||||
bool dump_file_is_internal_;
|
|
||||||
|
|
||||||
// Tracks whether the full dump file handle is managed externally.
|
|
||||||
bool full_dump_file_is_internal_;
|
|
||||||
|
|
||||||
// Additional streams to be written to the dump.
|
|
||||||
MINIDUMP_USER_STREAM_INFORMATION* additional_streams_;
|
|
||||||
|
|
||||||
// The user defined callback for the various stages of the dump process.
|
|
||||||
MINIDUMP_CALLBACK_INFORMATION* callback_info_;
|
|
||||||
|
|
||||||
// Critical section to sychronize action of loading modules dynamically.
|
|
||||||
CRITICAL_SECTION module_load_sync_;
|
|
||||||
|
|
||||||
// Critical section to synchronize action of dynamically getting function
|
|
||||||
// addresses from modules.
|
|
||||||
CRITICAL_SECTION get_proc_address_sync_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user