зеркало из https://github.com/mozilla/pjs.git
bug 381523 - upgrade breakpad to rev 174. r=bsmedberg
This commit is contained in:
Родитель
20a9494800
Коммит
942cb9f47b
|
@ -1,236 +0,0 @@
|
|||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
|
||||
Software Foundation, Inc.
|
||||
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
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 only 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. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes awhile. 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.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
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.
|
||||
|
||||
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=c89 CFLAGS=-O2 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 must use a version of `make' that
|
||||
supports the `VPATH' variable, such as 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 `..'.
|
||||
|
||||
If you have to use a `make' that does not support the `VPATH'
|
||||
variable, you have 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.
|
||||
|
||||
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'.
|
||||
|
||||
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.
|
||||
|
||||
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'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`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.
|
||||
|
||||
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). Here is a another example:
|
||||
|
||||
/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
|
||||
configuration-related scripts to be executed by `/bin/bash'.
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--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.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
|
@ -75,6 +75,8 @@ src_libbreakpad_la_SOURCES = \
|
|||
src/processor/contained_range_map.h \
|
||||
src/processor/contained_range_map-inl.h \
|
||||
src/processor/linked_ptr.h \
|
||||
src/processor/logging.h \
|
||||
src/processor/logging.cc \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/minidump_processor.cc \
|
||||
src/processor/pathname_stripper.cc \
|
||||
|
@ -126,14 +128,22 @@ TESTS_ENVIRONMENT =
|
|||
|
||||
src_processor_address_map_unittest_SOURCES = \
|
||||
src/processor/address_map_unittest.cc
|
||||
src_processor_address_map_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_basic_source_line_resolver_unittest_SOURCES = \
|
||||
src/processor/basic_source_line_resolver_unittest.cc
|
||||
src_processor_basic_source_line_resolver_unittest_LDADD = \
|
||||
src/processor/basic_source_line_resolver.lo
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/logging.lo
|
||||
|
||||
src_processor_contained_range_map_unittest_SOURCES = \
|
||||
src/processor/contained_range_map_unittest.cc
|
||||
src_processor_contained_range_map_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_minidump_processor_unittest_SOURCES = \
|
||||
src/processor/minidump_processor_unittest.cc
|
||||
|
@ -141,8 +151,10 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
|
@ -155,9 +167,15 @@ src_processor_pathname_stripper_unittest_LDADD = \
|
|||
|
||||
src_processor_postfix_evaluator_unittest_SOURCES = \
|
||||
src/processor/postfix_evaluator_unittest.cc
|
||||
src_processor_postfix_evaluator_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_range_map_unittest_SOURCES = \
|
||||
src/processor/range_map_unittest.cc
|
||||
src_processor_range_map_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_stackwalker_selftest_SOURCES = \
|
||||
src/processor/stackwalker_selftest.cc
|
||||
|
@ -165,7 +183,9 @@ src_processor_stackwalker_selftest_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
|
@ -178,7 +198,9 @@ src_processor_minidump_dump_SOURCES = \
|
|||
src/processor/minidump_dump.cc
|
||||
src_processor_minidump_dump_LDADD = \
|
||||
src/processor/basic_code_modules.lo \
|
||||
src/processor/minidump.lo
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_minidump_stackwalk_SOURCES = \
|
||||
src/processor/minidump_stackwalk.cc
|
||||
|
@ -186,6 +208,7 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Makefile.in generated by automake 1.9.6 from Makefile.am.
|
||||
# Makefile.in generated by automake 1.10 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
# This Makefile.in 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.
|
||||
|
@ -46,15 +46,11 @@
|
|||
|
||||
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = .
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
|
@ -81,19 +77,19 @@ check_PROGRAMS = src/processor/address_map_unittest$(EXEEXT) \
|
|||
@SELFTEST_TRUE@ src/processor/stackwalker_selftest
|
||||
|
||||
noinst_PROGRAMS =
|
||||
subdir = .
|
||||
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
|
||||
$(srcdir)/Makefile.am $(srcdir)/Makefile.in \
|
||||
$(top_srcdir)/configure $(top_srcdir)/src/config.h.in AUTHORS \
|
||||
COPYING ChangeLog INSTALL NEWS autotools/config.guess \
|
||||
autotools/config.sub autotools/depcomp autotools/install-sh \
|
||||
autotools/ltmain.sh autotools/missing
|
||||
subdir = .
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
|
||||
configure.lineno configure.status.lineno
|
||||
configure.lineno config.status.lineno
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/src/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
|
@ -111,8 +107,8 @@ src_libbreakpad_la_LIBADD =
|
|||
am__dirstamp = $(am__leading_dot)dirstamp
|
||||
am_src_libbreakpad_la_OBJECTS = src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/call_stack.lo src/processor/logging.lo \
|
||||
src/processor/minidump.lo src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/simple_symbol_supplier.lo \
|
||||
|
@ -127,23 +123,27 @@ am_src_processor_address_map_unittest_OBJECTS = \
|
|||
src/processor/address_map_unittest.$(OBJEXT)
|
||||
src_processor_address_map_unittest_OBJECTS = \
|
||||
$(am_src_processor_address_map_unittest_OBJECTS)
|
||||
src_processor_address_map_unittest_LDADD = $(LDADD)
|
||||
src_processor_address_map_unittest_DEPENDENCIES = \
|
||||
src/processor/logging.lo src/processor/pathname_stripper.lo
|
||||
am_src_processor_basic_source_line_resolver_unittest_OBJECTS = \
|
||||
src/processor/basic_source_line_resolver_unittest.$(OBJEXT)
|
||||
src_processor_basic_source_line_resolver_unittest_OBJECTS = $(am_src_processor_basic_source_line_resolver_unittest_OBJECTS)
|
||||
src_processor_basic_source_line_resolver_unittest_DEPENDENCIES = \
|
||||
src/processor/basic_source_line_resolver.lo
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/pathname_stripper.lo src/processor/logging.lo
|
||||
am_src_processor_contained_range_map_unittest_OBJECTS = \
|
||||
src/processor/contained_range_map_unittest.$(OBJEXT)
|
||||
src_processor_contained_range_map_unittest_OBJECTS = \
|
||||
$(am_src_processor_contained_range_map_unittest_OBJECTS)
|
||||
src_processor_contained_range_map_unittest_LDADD = $(LDADD)
|
||||
src_processor_contained_range_map_unittest_DEPENDENCIES = \
|
||||
src/processor/logging.lo src/processor/pathname_stripper.lo
|
||||
am_src_processor_minidump_dump_OBJECTS = \
|
||||
src/processor/minidump_dump.$(OBJEXT)
|
||||
src_processor_minidump_dump_OBJECTS = \
|
||||
$(am_src_processor_minidump_dump_OBJECTS)
|
||||
src_processor_minidump_dump_DEPENDENCIES = \
|
||||
src/processor/basic_code_modules.lo src/processor/minidump.lo
|
||||
src/processor/basic_code_modules.lo src/processor/logging.lo \
|
||||
src/processor/minidump.lo src/processor/pathname_stripper.lo
|
||||
am_src_processor_minidump_processor_unittest_OBJECTS = \
|
||||
src/processor/minidump_processor_unittest.$(OBJEXT)
|
||||
src_processor_minidump_processor_unittest_OBJECTS = \
|
||||
|
@ -151,8 +151,9 @@ src_processor_minidump_processor_unittest_OBJECTS = \
|
|||
src_processor_minidump_processor_unittest_DEPENDENCIES = \
|
||||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/call_stack.lo src/processor/logging.lo \
|
||||
src/processor/minidump_processor.lo src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
|
@ -163,8 +164,8 @@ src_processor_minidump_stackwalk_OBJECTS = \
|
|||
src_processor_minidump_stackwalk_DEPENDENCIES = \
|
||||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/call_stack.lo src/processor/logging.lo \
|
||||
src/processor/minidump.lo src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/simple_symbol_supplier.lo \
|
||||
|
@ -180,12 +181,14 @@ am_src_processor_postfix_evaluator_unittest_OBJECTS = \
|
|||
src/processor/postfix_evaluator_unittest.$(OBJEXT)
|
||||
src_processor_postfix_evaluator_unittest_OBJECTS = \
|
||||
$(am_src_processor_postfix_evaluator_unittest_OBJECTS)
|
||||
src_processor_postfix_evaluator_unittest_LDADD = $(LDADD)
|
||||
src_processor_postfix_evaluator_unittest_DEPENDENCIES = \
|
||||
src/processor/logging.lo src/processor/pathname_stripper.lo
|
||||
am_src_processor_range_map_unittest_OBJECTS = \
|
||||
src/processor/range_map_unittest.$(OBJEXT)
|
||||
src_processor_range_map_unittest_OBJECTS = \
|
||||
$(am_src_processor_range_map_unittest_OBJECTS)
|
||||
src_processor_range_map_unittest_LDADD = $(LDADD)
|
||||
src_processor_range_map_unittest_DEPENDENCIES = \
|
||||
src/processor/logging.lo src/processor/pathname_stripper.lo
|
||||
am_src_processor_stackwalker_selftest_OBJECTS = \
|
||||
src/processor/stackwalker_selftest.$(OBJEXT)
|
||||
src_processor_stackwalker_selftest_OBJECTS = \
|
||||
|
@ -193,29 +196,32 @@ src_processor_stackwalker_selftest_OBJECTS = \
|
|||
src_processor_stackwalker_selftest_DEPENDENCIES = \
|
||||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo src/processor/minidump.lo \
|
||||
src/processor/call_stack.lo src/processor/logging.lo \
|
||||
src/processor/minidump.lo src/processor/pathname_stripper.lo \
|
||||
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
SCRIPTS = $(noinst_SCRIPTS)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src
|
||||
DEFAULT_INCLUDES = -I. -I$(top_builddir)/src@am__isrc@
|
||||
depcomp = $(SHELL) $(top_srcdir)/autotools/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
CXXLD = $(CXX)
|
||||
CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
|
||||
$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
|
||||
$(LDFLAGS) -o $@
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CFLAGS) $(CFLAGS)
|
||||
LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
CCLD = $(CC)
|
||||
LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
|
||||
$(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
|
||||
$(LDFLAGS) -o $@
|
||||
SOURCES = $(src_libbreakpad_la_SOURCES) \
|
||||
$(src_processor_address_map_unittest_SOURCES) \
|
||||
$(src_processor_basic_source_line_resolver_unittest_SOURCES) \
|
||||
|
@ -254,8 +260,6 @@ GZIP_ENV = --best
|
|||
distuninstallcheck_listfiles = find . -type f -print
|
||||
distcleancheck_listfiles = find . -type f -print
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
|
@ -283,6 +287,7 @@ EXEEXT = @EXEEXT@
|
|||
F77 = @F77@
|
||||
FFLAGS = @FFLAGS@
|
||||
GREP = @GREP@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
|
@ -295,6 +300,7 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@
|
|||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
|
@ -304,19 +310,17 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
|||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
RANLIB = @RANLIB@
|
||||
SELFTEST_FALSE = @SELFTEST_FALSE@
|
||||
SELFTEST_TRUE = @SELFTEST_TRUE@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_F77 = @ac_ct_F77@
|
||||
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
|
||||
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
|
@ -328,6 +332,7 @@ build_alias = @build_alias@
|
|||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
|
||||
|
@ -355,8 +360,11 @@ program_transform_name = @program_transform_name@
|
|||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
|
||||
# This allows #includes to be relative to src/
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
|
@ -395,6 +403,8 @@ src_libbreakpad_la_SOURCES = \
|
|||
src/processor/contained_range_map.h \
|
||||
src/processor/contained_range_map-inl.h \
|
||||
src/processor/linked_ptr.h \
|
||||
src/processor/logging.h \
|
||||
src/processor/logging.cc \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/minidump_processor.cc \
|
||||
src/processor/pathname_stripper.cc \
|
||||
|
@ -424,15 +434,25 @@ TESTS_ENVIRONMENT =
|
|||
src_processor_address_map_unittest_SOURCES = \
|
||||
src/processor/address_map_unittest.cc
|
||||
|
||||
src_processor_address_map_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_basic_source_line_resolver_unittest_SOURCES = \
|
||||
src/processor/basic_source_line_resolver_unittest.cc
|
||||
|
||||
src_processor_basic_source_line_resolver_unittest_LDADD = \
|
||||
src/processor/basic_source_line_resolver.lo
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/logging.lo
|
||||
|
||||
src_processor_contained_range_map_unittest_SOURCES = \
|
||||
src/processor/contained_range_map_unittest.cc
|
||||
|
||||
src_processor_contained_range_map_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_minidump_processor_unittest_SOURCES = \
|
||||
src/processor/minidump_processor_unittest.cc
|
||||
|
||||
|
@ -440,8 +460,10 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
|
@ -456,9 +478,17 @@ src_processor_pathname_stripper_unittest_LDADD = \
|
|||
src_processor_postfix_evaluator_unittest_SOURCES = \
|
||||
src/processor/postfix_evaluator_unittest.cc
|
||||
|
||||
src_processor_postfix_evaluator_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_range_map_unittest_SOURCES = \
|
||||
src/processor/range_map_unittest.cc
|
||||
|
||||
src_processor_range_map_unittest_LDADD = \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_stackwalker_selftest_SOURCES = \
|
||||
src/processor/stackwalker_selftest.cc
|
||||
|
||||
|
@ -466,7 +496,9 @@ src_processor_stackwalker_selftest_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
|
@ -477,7 +509,9 @@ src_processor_minidump_dump_SOURCES = \
|
|||
|
||||
src_processor_minidump_dump_LDADD = \
|
||||
src/processor/basic_code_modules.lo \
|
||||
src/processor/minidump.lo
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
|
||||
src_processor_minidump_stackwalk_SOURCES = \
|
||||
src/processor/minidump_stackwalk.cc
|
||||
|
@ -486,6 +520,7 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
|
@ -611,7 +646,7 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
|||
src/config.h: src/stamp-h1
|
||||
@if test ! -f $@; then \
|
||||
rm -f src/stamp-h1; \
|
||||
$(MAKE) src/stamp-h1; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) src/stamp-h1; \
|
||||
else :; fi
|
||||
|
||||
src/stamp-h1: $(top_srcdir)/src/config.h.in $(top_builddir)/config.status
|
||||
|
@ -626,7 +661,7 @@ distclean-hdr:
|
|||
-rm -f src/config.h src/stamp-h1
|
||||
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
|
||||
test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
|
||||
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
|
||||
if test -f $$p; then \
|
||||
f=$(am__strip_dir) \
|
||||
|
@ -637,7 +672,7 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
|
|||
|
||||
uninstall-libLTLIBRARIES:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
|
||||
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
|
||||
p=$(am__strip_dir) \
|
||||
echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
|
||||
$(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
|
||||
|
@ -652,10 +687,10 @@ clean-libLTLIBRARIES:
|
|||
rm -f "$${dir}/so_locations"; \
|
||||
done
|
||||
src/processor/$(am__dirstamp):
|
||||
@$(mkdir_p) src/processor
|
||||
@$(MKDIR_P) src/processor
|
||||
@: > src/processor/$(am__dirstamp)
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp):
|
||||
@$(mkdir_p) src/processor/$(DEPDIR)
|
||||
@$(MKDIR_P) src/processor/$(DEPDIR)
|
||||
@: > src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/basic_code_modules.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
|
@ -664,6 +699,8 @@ src/processor/basic_source_line_resolver.lo: \
|
|||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/call_stack.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/logging.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/minidump.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/minidump_processor.lo: src/processor/$(am__dirstamp) \
|
||||
|
@ -682,13 +719,13 @@ src/processor/stackwalker_ppc.lo: src/processor/$(am__dirstamp) \
|
|||
src/processor/stackwalker_x86.lo: src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/$(am__dirstamp):
|
||||
@$(mkdir_p) src
|
||||
@$(MKDIR_P) src
|
||||
@: > src/$(am__dirstamp)
|
||||
src/libbreakpad.la: $(src_libbreakpad_la_OBJECTS) $(src_libbreakpad_la_DEPENDENCIES) src/$(am__dirstamp)
|
||||
$(CXXLINK) -rpath $(libdir) $(src_libbreakpad_la_LDFLAGS) $(src_libbreakpad_la_OBJECTS) $(src_libbreakpad_la_LIBADD) $(LIBS)
|
||||
$(CXXLINK) -rpath $(libdir) $(src_libbreakpad_la_OBJECTS) $(src_libbreakpad_la_LIBADD) $(LIBS)
|
||||
install-binPROGRAMS: $(bin_PROGRAMS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
|
||||
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
|
||||
@list='$(bin_PROGRAMS)'; for p in $$list; do \
|
||||
p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
|
||||
if test -f $$p \
|
||||
|
@ -733,60 +770,60 @@ src/processor/address_map_unittest.$(OBJEXT): \
|
|||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/address_map_unittest$(EXEEXT): $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/address_map_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_address_map_unittest_LDFLAGS) $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_LDADD) $(LIBS)
|
||||
src/processor/basic_source_line_resolver_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/basic_source_line_resolver_unittest$(EXEEXT): $(src_processor_basic_source_line_resolver_unittest_OBJECTS) $(src_processor_basic_source_line_resolver_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/basic_source_line_resolver_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_basic_source_line_resolver_unittest_LDFLAGS) $(src_processor_basic_source_line_resolver_unittest_OBJECTS) $(src_processor_basic_source_line_resolver_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_basic_source_line_resolver_unittest_OBJECTS) $(src_processor_basic_source_line_resolver_unittest_LDADD) $(LIBS)
|
||||
src/processor/contained_range_map_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/contained_range_map_unittest$(EXEEXT): $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/contained_range_map_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_contained_range_map_unittest_LDFLAGS) $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_LDADD) $(LIBS)
|
||||
src/processor/minidump_dump.$(OBJEXT): src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/minidump_dump$(EXEEXT): $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/minidump_dump$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_minidump_dump_LDFLAGS) $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_LDADD) $(LIBS)
|
||||
src/processor/minidump_processor_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/minidump_processor_unittest$(EXEEXT): $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/minidump_processor_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_minidump_processor_unittest_LDFLAGS) $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_LDADD) $(LIBS)
|
||||
src/processor/minidump_stackwalk.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/minidump_stackwalk$(EXEEXT): $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/minidump_stackwalk$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_minidump_stackwalk_LDFLAGS) $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_LDADD) $(LIBS)
|
||||
src/processor/pathname_stripper_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/pathname_stripper_unittest$(EXEEXT): $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/pathname_stripper_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_pathname_stripper_unittest_LDFLAGS) $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_LDADD) $(LIBS)
|
||||
src/processor/postfix_evaluator_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/postfix_evaluator_unittest$(EXEEXT): $(src_processor_postfix_evaluator_unittest_OBJECTS) $(src_processor_postfix_evaluator_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/postfix_evaluator_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_postfix_evaluator_unittest_LDFLAGS) $(src_processor_postfix_evaluator_unittest_OBJECTS) $(src_processor_postfix_evaluator_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_postfix_evaluator_unittest_OBJECTS) $(src_processor_postfix_evaluator_unittest_LDADD) $(LIBS)
|
||||
src/processor/range_map_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/range_map_unittest$(EXEEXT): $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/range_map_unittest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_range_map_unittest_LDFLAGS) $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_LDADD) $(LIBS)
|
||||
src/processor/stackwalker_selftest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/stackwalker_selftest$(EXEEXT): $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/stackwalker_selftest$(EXEEXT)
|
||||
$(CXXLINK) $(src_processor_stackwalker_selftest_LDFLAGS) $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_LDADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
@ -799,6 +836,8 @@ mostlyclean-compile:
|
|||
-rm -f src/processor/call_stack.$(OBJEXT)
|
||||
-rm -f src/processor/call_stack.lo
|
||||
-rm -f src/processor/contained_range_map_unittest.$(OBJEXT)
|
||||
-rm -f src/processor/logging.$(OBJEXT)
|
||||
-rm -f src/processor/logging.lo
|
||||
-rm -f src/processor/minidump.$(OBJEXT)
|
||||
-rm -f src/processor/minidump.lo
|
||||
-rm -f src/processor/minidump_dump.$(OBJEXT)
|
||||
|
@ -832,6 +871,7 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/basic_source_line_resolver_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/call_stack.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/contained_range_map_unittest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/logging.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_dump.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor.Plo@am__quote@
|
||||
|
@ -849,25 +889,25 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86.Plo@am__quote@
|
||||
|
||||
.cc.o:
|
||||
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`; \
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$$depbase.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$$depbase.Tpo" "$$depbase.Po"; else rm -f "$$depbase.Tpo"; exit 1; fi
|
||||
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCXX_TRUE@ mv -f $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cc.obj:
|
||||
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`; \
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$$depbase.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$$depbase.Tpo" "$$depbase.Po"; else rm -f "$$depbase.Tpo"; exit 1; fi
|
||||
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
|
||||
@am__fastdepCXX_TRUE@ mv -f $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.cc.lo:
|
||||
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`; \
|
||||
@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$$depbase.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$$depbase.Tpo" "$$depbase.Plo"; else rm -f "$$depbase.Tpo"; exit 1; fi
|
||||
@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCXX_TRUE@ mv -f $$depbase.Tpo $$depbase.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
|
||||
|
@ -882,10 +922,9 @@ clean-libtool:
|
|||
|
||||
distclean-libtool:
|
||||
-rm -f libtool
|
||||
uninstall-info-am:
|
||||
install-dist_docDATA: $(dist_doc_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)"
|
||||
test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)"
|
||||
@list='$(dist_doc_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
|
@ -950,9 +989,9 @@ distclean-tags:
|
|||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
check-TESTS: $(TESTS)
|
||||
@failed=0; all=0; xfail=0; xpass=0; skip=0; \
|
||||
@failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \
|
||||
srcdir=$(srcdir); export srcdir; \
|
||||
list='$(TESTS)'; \
|
||||
list=' $(TESTS) '; \
|
||||
if test -n "$$list"; then \
|
||||
for tst in $$list; do \
|
||||
if test -f ./$$tst; then dir=./; \
|
||||
|
@ -961,7 +1000,7 @@ check-TESTS: $(TESTS)
|
|||
if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
|
||||
all=`expr $$all + 1`; \
|
||||
case " $(XFAIL_TESTS) " in \
|
||||
*" $$tst "*) \
|
||||
*$$ws$$tst$$ws*) \
|
||||
xpass=`expr $$xpass + 1`; \
|
||||
failed=`expr $$failed + 1`; \
|
||||
echo "XPASS: $$tst"; \
|
||||
|
@ -973,7 +1012,7 @@ check-TESTS: $(TESTS)
|
|||
elif test $$? -ne 77; then \
|
||||
all=`expr $$all + 1`; \
|
||||
case " $(XFAIL_TESTS) " in \
|
||||
*" $$tst "*) \
|
||||
*$$ws$$tst$$ws*) \
|
||||
xfail=`expr $$xfail + 1`; \
|
||||
echo "XFAIL: $$tst"; \
|
||||
;; \
|
||||
|
@ -1024,24 +1063,22 @@ check-TESTS: $(TESTS)
|
|||
|
||||
distdir: $(DISTFILES)
|
||||
$(am__remove_distdir)
|
||||
mkdir $(distdir)
|
||||
$(mkdir_p) $(distdir)/autotools $(distdir)/src $(distdir)/src/client $(distdir)/src/client/mac/handler $(distdir)/src/client/mac/handler/minidump_test.xcodeproj $(distdir)/src/client/windows $(distdir)/src/client/windows/handler $(distdir)/src/client/windows/sender $(distdir)/src/common $(distdir)/src/common/mac $(distdir)/src/common/windows $(distdir)/src/processor $(distdir)/src/processor/testdata $(distdir)/src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542 $(distdir)/src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1 $(distdir)/src/tools/mac/crash_report $(distdir)/src/tools/mac/crash_report/crash_report.xcodeproj $(distdir)/src/tools/mac/dump_syms $(distdir)/src/tools/mac/dump_syms/dump_syms.xcodeproj $(distdir)/src/tools/mac/symupload $(distdir)/src/tools/mac/symupload/symupload.xcodeproj $(distdir)/src/tools/windows/converter $(distdir)/src/tools/windows/dump_syms $(distdir)/src/tools/windows/dump_syms/testdata $(distdir)/src/tools/windows/symupload
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
test -d $(distdir) || mkdir $(distdir)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
|
@ -1055,7 +1092,7 @@ distdir: $(DISTFILES)
|
|||
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
|
||||
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
|
||||
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|
||||
|| chmod -R a+r $(distdir)
|
||||
dist-gzip: distdir
|
||||
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
|
||||
|
@ -1130,7 +1167,7 @@ distcheck: dist
|
|||
$(am__remove_distdir)
|
||||
@(echo "$(distdir) archives ready for distribution: "; \
|
||||
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
|
||||
sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
|
||||
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
|
||||
distuninstallcheck:
|
||||
@cd $(distuninstallcheck_dir) \
|
||||
&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
|
||||
|
@ -1158,7 +1195,7 @@ install-binPROGRAMS: install-libLTLIBRARIES
|
|||
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(docdir)"; do \
|
||||
test -z "$$dir" || $(mkdir_p) "$$dir"; \
|
||||
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
|
||||
done
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
|
@ -1212,12 +1249,20 @@ info-am:
|
|||
|
||||
install-data-am: install-dist_docDATA
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
|
@ -1241,7 +1286,9 @@ ps: ps-am
|
|||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
|
||||
uninstall-info-am uninstall-libLTLIBRARIES
|
||||
uninstall-libLTLIBRARIES
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \
|
||||
clean clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
|
||||
|
@ -1252,14 +1299,15 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
|
|||
distclean-tags distcleancheck distdir distuninstallcheck dvi \
|
||||
dvi-am html html-am info info-am install install-am \
|
||||
install-binPROGRAMS install-data install-data-am \
|
||||
install-dist_docDATA install-exec install-exec-am install-info \
|
||||
install-info-am install-libLTLIBRARIES install-man \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags uninstall uninstall-am \
|
||||
uninstall-binPROGRAMS uninstall-dist_docDATA uninstall-info-am \
|
||||
uninstall-libLTLIBRARIES
|
||||
install-dist_docDATA install-dvi install-dvi-am install-exec \
|
||||
install-exec-am install-html install-html-am install-info \
|
||||
install-info-am install-libLTLIBRARIES install-man install-pdf \
|
||||
install-pdf-am install-ps install-ps-am install-strip \
|
||||
installcheck installcheck-am installdirs maintainer-clean \
|
||||
maintainer-clean-generic mostlyclean mostlyclean-compile \
|
||||
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
|
||||
tags uninstall uninstall-am uninstall-binPROGRAMS \
|
||||
uninstall-dist_docDATA uninstall-libLTLIBRARIES
|
||||
|
||||
|
||||
libtool: $(LIBTOOL_DEPS)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,530 +0,0 @@
|
|||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2005-07-09.11
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# 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 outputing dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
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"
|
||||
|
||||
# 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
|
||||
|
||||
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.
|
||||
"$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## 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).
|
||||
## - 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 -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the `deleted header file' problem.
|
||||
## 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.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the `:'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
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 -eq 0; then :
|
||||
else
|
||||
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 ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr '
|
||||
' ' ' >> $depfile
|
||||
echo >> $depfile
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> $depfile
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
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.
|
||||
stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
|
||||
tmpdepfile="$stripped.u"
|
||||
if test "$libtool" = yes; then
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
|
||||
if test -f "$tmpdepfile"; then :
|
||||
else
|
||||
stripped=`echo "$stripped" | sed 's,^.*/,,'`
|
||||
tmpdepfile="$stripped.u"
|
||||
fi
|
||||
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
if test -f "$tmpdepfile"; then
|
||||
outname="$stripped.o"
|
||||
# Each line is of the form `foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
icc)
|
||||
# Intel's C compiler understands `-MD -MF file'. However on
|
||||
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want:
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using \ :
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
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"
|
||||
;;
|
||||
|
||||
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.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mecanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
# That's a tab and a space in the [].
|
||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# 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 $1 != '--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:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
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 $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no
|
||||
for arg in "$@"; do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
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.
|
||||
-*|$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"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' '
|
||||
' | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
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 $1 != '--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, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
"$@" || exit $?
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
|
||||
echo " " >> "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
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-end: "$"
|
||||
# End:
|
|
@ -1,323 +0,0 @@
|
|||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2005-05-14.22
|
||||
|
||||
# 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. It can only install one file at a time, a restriction
|
||||
# shared with many OS's install programs.
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=
|
||||
chgrpcmd=
|
||||
stripcmd=
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dstarg=
|
||||
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:
|
||||
-c (ignored)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test -n "$1"; do
|
||||
case $1 in
|
||||
-c) shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd=$stripprog
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t) dstarg=$2
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-T) no_target_directory=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
*) # When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
test -n "$dir_arg$dstarg" && break
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dstarg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dstarg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dstarg=$arg
|
||||
done
|
||||
break;;
|
||||
esac
|
||||
done
|
||||
|
||||
if test -z "$1"; 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
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names starting with `-'.
|
||||
case $src in
|
||||
-*) src=./$src ;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
src=
|
||||
|
||||
if test -d "$dst"; then
|
||||
mkdircmd=:
|
||||
chmodcmd=
|
||||
else
|
||||
mkdircmd=$mkdirprog
|
||||
fi
|
||||
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 "$dstarg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dst=$dstarg
|
||||
# Protect names starting with `-'.
|
||||
case $dst in
|
||||
-*) dst=./$dst ;;
|
||||
esac
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dstarg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst/`basename "$src"`
|
||||
fi
|
||||
fi
|
||||
|
||||
# This sed command emulates the dirname command.
|
||||
dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if test ! -d "$dstdir"; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-$defaultIFS}"
|
||||
|
||||
oIFS=$IFS
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
shift
|
||||
IFS=$oIFS
|
||||
|
||||
pathcomp=
|
||||
|
||||
while test $# -ne 0 ; do
|
||||
pathcomp=$pathcomp$1
|
||||
shift
|
||||
if test ! -d "$pathcomp"; then
|
||||
$mkdirprog "$pathcomp"
|
||||
# mkdir can fail with a `File exist' error in case several
|
||||
# install-sh are creating the directory concurrently. This
|
||||
# is OK.
|
||||
test -d "$pathcomp" || exit
|
||||
fi
|
||||
pathcomp=$pathcomp/
|
||||
done
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
$doit $mkdircmd "$dst" \
|
||||
&& { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
|
||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
|
||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
|
||||
&& { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
|
||||
|
||||
else
|
||||
dstfile=`basename "$dst"`
|
||||
|
||||
# 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
|
||||
trap '(exit $?); exit' 1 2 13 15
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
$doit $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 "$dsttmp"; } &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
{ $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 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.
|
||||
{
|
||||
if test -f "$dstdir/$dstfile"; then
|
||||
$doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
|
||||
|| $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
|
||||
|| {
|
||||
echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
else
|
||||
:
|
||||
fi
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
|
||||
}
|
||||
}
|
||||
fi || { (exit 1); exit 1; }
|
||||
done
|
||||
|
||||
# The final little trick to "correctly" pass the exit status to the exit trap.
|
||||
{
|
||||
(exit 0); exit 0
|
||||
}
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
|
@ -1,360 +0,0 @@
|
|||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
|
||||
scriptversion=2005-06-08.21
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case "$1" in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
tar try tar, gnutar, gtar, then tar without non-portable flags
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-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
|
||||
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program).
|
||||
case "$1" in
|
||||
lex|yacc)
|
||||
# Not GNU programs, they don't have --version.
|
||||
;;
|
||||
|
||||
tar)
|
||||
if test -n "$run"; then
|
||||
echo 1>&2 "ERROR: \`tar' requires --run"
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case "$1" in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case "$f" in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
|
||||
test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison|yacc)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if [ $# -ne 1 ]; then
|
||||
eval LASTARG="\${$#}"
|
||||
case "$LASTARG" in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if [ -f "$SRCFILE" ]; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if [ -f "$SRCFILE" ]; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ ! -f y.tab.h ]; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if [ ! -f y.tab.c ]; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex|flex)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if [ $# -ne 1 ]; then
|
||||
eval LASTARG="\${$#}"
|
||||
case "$LASTARG" in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if [ -f "$SRCFILE" ]; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ ! -f lex.yy.c ]; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
|
||||
if test -z "$file"; then
|
||||
file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
|
||||
fi
|
||||
if [ -f "$file" ]; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
tar)
|
||||
shift
|
||||
|
||||
# We have already tried tar in the generic part.
|
||||
# Look for gnutar/gtar before invocation to avoid ugly error
|
||||
# messages.
|
||||
if (gnutar --version > /dev/null 2>&1); then
|
||||
gnutar "$@" && exit 0
|
||||
fi
|
||||
if (gtar --version > /dev/null 2>&1); then
|
||||
gtar "$@" && exit 0
|
||||
fi
|
||||
firstarg="$1"
|
||||
if shift; then
|
||||
case "$firstarg" in
|
||||
*o*)
|
||||
firstarg=`echo "$firstarg" | sed s/o//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
case "$firstarg" in
|
||||
*h*)
|
||||
firstarg=`echo "$firstarg" | sed s/h//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: I can't seem to be able to run \`tar' with the given arguments.
|
||||
You may want to install GNU tar or Free paxutils, or check the
|
||||
command line arguments."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,239 +0,0 @@
|
|||
// Copyright (c) 2007, 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 <mach-o/nlist.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "client/mac/handler/dynamic_images.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//==============================================================================
|
||||
// Reads an address range from another task. A block of memory is malloced
|
||||
// and should be freed by the caller.
|
||||
void* ReadTaskMemory(task_port_t target_task,
|
||||
const void* address,
|
||||
size_t length) {
|
||||
void* result = NULL;
|
||||
vm_address_t page_address = reinterpret_cast<vm_address_t>(address) & (-4096);
|
||||
vm_address_t last_page_address =
|
||||
(reinterpret_cast<vm_address_t>(address) + length + 4095) & (-4096);
|
||||
vm_size_t page_size = last_page_address - page_address;
|
||||
uint8_t* local_start;
|
||||
uint32_t local_length;
|
||||
|
||||
kern_return_t r = vm_read(target_task,
|
||||
page_address,
|
||||
page_size,
|
||||
reinterpret_cast<vm_offset_t*>(&local_start),
|
||||
&local_length);
|
||||
|
||||
if (r == KERN_SUCCESS) {
|
||||
result = malloc(length);
|
||||
if (result != NULL) {
|
||||
memcpy(result, &local_start[(uint32_t)address - page_address], length);
|
||||
}
|
||||
vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
//==============================================================================
|
||||
// Initializes vmaddr_, vmsize_, and slide_
|
||||
void DynamicImage::CalculateMemoryInfo() {
|
||||
mach_header *header = GetMachHeader();
|
||||
|
||||
const struct load_command *cmd =
|
||||
reinterpret_cast<const struct load_command *>(header + 1);
|
||||
|
||||
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
|
||||
if (cmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command *seg =
|
||||
reinterpret_cast<const struct segment_command *>(cmd);
|
||||
|
||||
if (!strcmp(seg->segname, "__TEXT")) {
|
||||
vmaddr_ = seg->vmaddr;
|
||||
vmsize_ = seg->vmsize;
|
||||
slide_ = 0;
|
||||
|
||||
if (seg->fileoff == 0 && seg->filesize != 0) {
|
||||
slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cmd = reinterpret_cast<const struct load_command *>
|
||||
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
|
||||
}
|
||||
|
||||
// we failed - a call to IsValid() will return false
|
||||
vmaddr_ = 0;
|
||||
vmsize_ = 0;
|
||||
slide_ = 0;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
//==============================================================================
|
||||
// Loads information about dynamically loaded code in the given task.
|
||||
DynamicImages::DynamicImages(mach_port_t task)
|
||||
: task_(task) {
|
||||
ReadImageInfoForTask();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This code was written using dyld_debug.c (from Darwin) as a guide.
|
||||
void DynamicImages::ReadImageInfoForTask() {
|
||||
struct nlist l[8];
|
||||
memset(l, 0, sizeof(l) );
|
||||
|
||||
// First we lookup the address of the "_dyld_all_image_infos" struct
|
||||
// which lives in "dyld". This structure contains information about all
|
||||
// of the loaded dynamic images.
|
||||
struct nlist &list = l[0];
|
||||
list.n_un.n_name = "_dyld_all_image_infos";
|
||||
nlist("/usr/lib/dyld", &list);
|
||||
|
||||
if (list.n_value) {
|
||||
// Read the structure inside of dyld that contains information about
|
||||
// loaded images. We're reading from the desired task's address space.
|
||||
|
||||
// Here we make the assumption that dyld loaded at the same address in
|
||||
// the crashed process vs. this one. This is an assumption made in
|
||||
// "dyld_debug.c" and is said to be nearly always valid.
|
||||
dyld_all_image_infos *dyldInfo = reinterpret_cast<dyld_all_image_infos*>
|
||||
(ReadTaskMemory(task_,
|
||||
reinterpret_cast<void*>(list.n_value),
|
||||
sizeof(dyld_all_image_infos)));
|
||||
|
||||
if (dyldInfo) {
|
||||
// number of loaded images
|
||||
int count = dyldInfo->infoArrayCount;
|
||||
|
||||
// Read an array of dyld_image_info structures each containing
|
||||
// information about a loaded image.
|
||||
dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*>
|
||||
(ReadTaskMemory(task_,
|
||||
dyldInfo->infoArray,
|
||||
count*sizeof(dyld_image_info)));
|
||||
|
||||
image_list_.reserve(count);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dyld_image_info &info = infoArray[i];
|
||||
|
||||
// First read just the mach_header from the image in the task.
|
||||
mach_header *header = reinterpret_cast<mach_header*>
|
||||
(ReadTaskMemory(task_, info.load_address_, sizeof(mach_header)));
|
||||
|
||||
if (!header)
|
||||
break; // bail on this dynamic image
|
||||
|
||||
// Now determine the total amount we really want to read based on the
|
||||
// size of the load commands. We need the header plus all of the
|
||||
// load commands.
|
||||
unsigned int header_size = sizeof(mach_header) + header->sizeofcmds;
|
||||
free(header);
|
||||
|
||||
header = reinterpret_cast<mach_header*>
|
||||
(ReadTaskMemory(task_, info.load_address_, header_size));
|
||||
|
||||
// Read the file name from the task's memory space.
|
||||
char *file_path = NULL;
|
||||
if (info.file_path_) {
|
||||
// Although we're reading 0x2000 bytes, this is copied in the
|
||||
// the DynamicImage constructor below with the correct string length,
|
||||
// so it's not really wasting memory.
|
||||
file_path = reinterpret_cast<char*>
|
||||
(ReadTaskMemory(task_,
|
||||
info.file_path_,
|
||||
0x2000));
|
||||
}
|
||||
|
||||
// Create an object representing this image and add it to our list.
|
||||
DynamicImage *new_image = new DynamicImage(header,
|
||||
header_size,
|
||||
info.load_address_,
|
||||
file_path,
|
||||
info.file_mod_date_,
|
||||
task_);
|
||||
|
||||
if (new_image->IsValid()) {
|
||||
image_list_.push_back(DynamicImageRef(new_image));
|
||||
} else {
|
||||
delete new_image;
|
||||
}
|
||||
|
||||
if (file_path) {
|
||||
free(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
free(dyldInfo);
|
||||
free(infoArray);
|
||||
|
||||
// sorts based on loading address
|
||||
sort(image_list_.begin(), image_list_.end() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
DynamicImage *DynamicImages::GetExecutableImage() {
|
||||
int executable_index = GetExecutableImageIndex();
|
||||
|
||||
if (executable_index >= 0) {
|
||||
return GetImage(executable_index);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// returns -1 if failure to find executable
|
||||
int DynamicImages::GetExecutableImageIndex() {
|
||||
int image_count = GetImageCount();
|
||||
|
||||
for (int i = 0; i < image_count; ++i) {
|
||||
DynamicImage *image = GetImage(i);
|
||||
if (image->GetMachHeader()->filetype == MH_EXECUTE) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -147,19 +147,8 @@ class DynamicImage {
|
|||
}
|
||||
|
||||
// Debugging
|
||||
void Print() {
|
||||
char *path = GetFilePath();
|
||||
if (!path) {
|
||||
path = "(unknown)";
|
||||
}
|
||||
printf("%p: %s\n", GetLoadAddress(), path);
|
||||
mach_header *header = GetMachHeader();
|
||||
MachHeader(*header).Print();
|
||||
printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
|
||||
printf("vmsize\t\t: %d\n", GetVMSize());
|
||||
printf("slide\t\t: %d\n", GetVMAddrSlide());
|
||||
}
|
||||
|
||||
void Print();
|
||||
|
||||
private:
|
||||
friend class DynamicImages;
|
||||
|
||||
|
|
|
@ -1,727 +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.
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <mach/host_info.h>
|
||||
#include <mach/vm_statistics.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "client/minidump_file_writer-inl.h"
|
||||
#include "common/mac/file_id.h"
|
||||
#include "common/mac/string_utilities.h"
|
||||
|
||||
using MacStringUtils::ConvertToString;
|
||||
using MacStringUtils::IntegerValueAtIndex;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
MinidumpGenerator::MinidumpGenerator()
|
||||
: exception_type_(0),
|
||||
exception_code_(0),
|
||||
exception_thread_(0),
|
||||
crashing_task_(mach_task_self()),
|
||||
handler_thread_(mach_thread_self()) {
|
||||
dynamic_images_ = new DynamicImages(mach_task_self());
|
||||
GatherSystemInformation();
|
||||
}
|
||||
|
||||
MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread)
|
||||
: exception_type_(0),
|
||||
exception_code_(0),
|
||||
exception_thread_(0),
|
||||
crashing_task_(crashing_task),
|
||||
handler_thread_(handler_thread) {
|
||||
dynamic_images_ = new DynamicImages(crashing_task_);
|
||||
GatherSystemInformation();
|
||||
}
|
||||
|
||||
MinidumpGenerator::~MinidumpGenerator() {
|
||||
}
|
||||
|
||||
char MinidumpGenerator::build_string_[16];
|
||||
int MinidumpGenerator::os_major_version_ = 0;
|
||||
int MinidumpGenerator::os_minor_version_ = 0;
|
||||
int MinidumpGenerator::os_build_number_ = 0;
|
||||
|
||||
// static
|
||||
void MinidumpGenerator::GatherSystemInformation() {
|
||||
// If this is non-zero, then we've already gathered the information
|
||||
if (os_major_version_)
|
||||
return;
|
||||
|
||||
// This code extracts the version and build information from the OS
|
||||
CFStringRef vers_path =
|
||||
CFSTR("/System/Library/CoreServices/SystemVersion.plist");
|
||||
CFURLRef sys_vers =
|
||||
CFURLCreateWithFileSystemPath(NULL, vers_path, kCFURLPOSIXPathStyle, false);
|
||||
CFDataRef data;
|
||||
SInt32 error;
|
||||
CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
|
||||
&error);
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
CFDictionaryRef list = static_cast<CFDictionaryRef>
|
||||
(CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
|
||||
NULL));
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
CFStringRef build_version = static_cast<CFStringRef>
|
||||
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
|
||||
CFStringRef product_version = static_cast<CFStringRef>
|
||||
(CFDictionaryGetValue(list, CFSTR("ProductVersion")));
|
||||
string build_str = ConvertToString(build_version);
|
||||
string product_str = ConvertToString(product_version);
|
||||
|
||||
CFRelease(list);
|
||||
CFRelease(sys_vers);
|
||||
CFRelease(data);
|
||||
|
||||
strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
|
||||
|
||||
// Parse the string that looks like "10.4.8"
|
||||
os_major_version_ = IntegerValueAtIndex(product_str, 0);
|
||||
os_minor_version_ = IntegerValueAtIndex(product_str, 1);
|
||||
os_build_number_ = IntegerValueAtIndex(product_str, 2);
|
||||
}
|
||||
|
||||
string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
|
||||
string *unique_name) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(NULL);
|
||||
CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
|
||||
CFRelease(uuid);
|
||||
string file_name(ConvertToString(uuid_cfstr));
|
||||
CFRelease(uuid_cfstr);
|
||||
string path(dir);
|
||||
|
||||
// Ensure that the directory (if non-empty) has a trailing slash so that
|
||||
// we can append the file name and have a valid pathname.
|
||||
if (!dir.empty()) {
|
||||
if (dir.at(dir.size() - 1) != '/')
|
||||
path.append(1, '/');
|
||||
}
|
||||
|
||||
path.append(file_name);
|
||||
path.append(".dmp");
|
||||
|
||||
if (unique_name)
|
||||
*unique_name = file_name;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::Write(const char *path) {
|
||||
WriteStreamFN writers[] = {
|
||||
&MinidumpGenerator::WriteThreadListStream,
|
||||
&MinidumpGenerator::WriteSystemInfoStream,
|
||||
&MinidumpGenerator::WriteModuleListStream,
|
||||
&MinidumpGenerator::WriteMiscInfoStream,
|
||||
&MinidumpGenerator::WriteBreakpadInfoStream,
|
||||
// Exception stream needs to be the last entry in this array as it may
|
||||
// be omitted in the case where the minidump is written without an
|
||||
// exception.
|
||||
&MinidumpGenerator::WriteExceptionStream,
|
||||
};
|
||||
bool result = true;
|
||||
|
||||
// If opening was successful, create the header, directory, and call each
|
||||
// writer. The destructor for the TypedMDRVAs will cause the data to be
|
||||
// flushed. The destructor for the MinidumpFileWriter will close the file.
|
||||
if (writer_.Open(path)) {
|
||||
TypedMDRVA<MDRawHeader> header(&writer_);
|
||||
TypedMDRVA<MDRawDirectory> dir(&writer_);
|
||||
|
||||
if (!header.Allocate())
|
||||
return false;
|
||||
|
||||
int writer_count = sizeof(writers) / sizeof(writers[0]);
|
||||
|
||||
// If we don't have exception information, don't write out the
|
||||
// exception stream
|
||||
if (!exception_thread_ && !exception_type_)
|
||||
--writer_count;
|
||||
|
||||
// Add space for all writers
|
||||
if (!dir.AllocateArray(writer_count))
|
||||
return false;
|
||||
|
||||
MDRawHeader *header_ptr = header.get();
|
||||
header_ptr->signature = MD_HEADER_SIGNATURE;
|
||||
header_ptr->version = MD_HEADER_VERSION;
|
||||
time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
|
||||
header_ptr->stream_count = writer_count;
|
||||
header_ptr->stream_directory_rva = dir.position();
|
||||
|
||||
MDRawDirectory local_dir;
|
||||
for (int i = 0; (result) && (i < writer_count); ++i) {
|
||||
result = (this->*writers[i])(&local_dir);
|
||||
|
||||
if (result)
|
||||
dir.CopyIndex(i, &local_dir);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t MinidumpGenerator::CalculateStackSize(vm_address_t start_addr) {
|
||||
vm_address_t stack_region_base = start_addr;
|
||||
vm_size_t stack_region_size;
|
||||
natural_t nesting_level = 0;
|
||||
vm_region_submap_info submap_info;
|
||||
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT;
|
||||
kern_return_t result =
|
||||
vm_region_recurse(crashing_task_, &stack_region_base, &stack_region_size,
|
||||
&nesting_level,
|
||||
reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
|
||||
&info_count);
|
||||
|
||||
if ((stack_region_base + stack_region_size) == 0xbffff000) {
|
||||
// The stack for thread 0 needs to extend all the way to 0xc0000000
|
||||
// For many processes the stack is first created in one page
|
||||
// from 0xbffff000 - 0xc0000000 and is then later extended to
|
||||
// a much larger size by creating a new VM region immediately below
|
||||
// the initial page
|
||||
|
||||
// include the original stack frame page (0xbffff000 - 0xc0000000)
|
||||
stack_region_size += 0x1000;
|
||||
}
|
||||
|
||||
return result == KERN_SUCCESS ?
|
||||
stack_region_base + stack_region_size - start_addr : 0;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteStackFromStartAddress(
|
||||
vm_address_t start_addr,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
UntypedMDRVA memory(&writer_);
|
||||
size_t size = CalculateStackSize(start_addr);
|
||||
|
||||
// If there's an error in the calculation, return at least the current
|
||||
// stack information
|
||||
if (size == 0)
|
||||
size = 16;
|
||||
|
||||
if (!memory.Allocate(size))
|
||||
return false;
|
||||
|
||||
void *stack_memory = ReadTaskMemory(crashing_task_, (void*)start_addr, size);
|
||||
|
||||
bool result = memory.Copy(stack_memory, size);
|
||||
|
||||
free(stack_memory);
|
||||
|
||||
|
||||
stack_location->start_of_memory_range = start_addr;
|
||||
stack_location->memory = memory.location();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if TARGET_CPU_PPC
|
||||
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
ppc_thread_state_t *machine_state =
|
||||
reinterpret_cast<ppc_thread_state_t *>(state);
|
||||
vm_address_t start_addr = machine_state->r1;
|
||||
return WriteStackFromStartAddress(start_addr, stack_location);
|
||||
}
|
||||
|
||||
u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
|
||||
ppc_thread_state_t *machine_state =
|
||||
reinterpret_cast<ppc_thread_state_t *>(state);
|
||||
|
||||
return machine_state->srr0;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location) {
|
||||
TypedMDRVA<MDRawContextPPC> context(&writer_);
|
||||
ppc_thread_state_t *machine_state =
|
||||
reinterpret_cast<ppc_thread_state_t *>(state);
|
||||
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
|
||||
*register_location = context.location();
|
||||
MDRawContextPPC *context_ptr = context.get();
|
||||
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
|
||||
|
||||
#define AddReg(a) context_ptr->a = machine_state->a
|
||||
#define AddGPR(a) context_ptr->gpr[a] = machine_state->r ## a
|
||||
AddReg(srr0);
|
||||
AddReg(cr);
|
||||
AddReg(xer);
|
||||
AddReg(ctr);
|
||||
AddReg(mq);
|
||||
AddReg(lr);
|
||||
AddReg(vrsave);
|
||||
|
||||
AddGPR(0);
|
||||
AddGPR(1);
|
||||
AddGPR(2);
|
||||
AddGPR(3);
|
||||
AddGPR(4);
|
||||
AddGPR(5);
|
||||
AddGPR(6);
|
||||
AddGPR(7);
|
||||
AddGPR(8);
|
||||
AddGPR(9);
|
||||
AddGPR(10);
|
||||
AddGPR(11);
|
||||
AddGPR(12);
|
||||
AddGPR(13);
|
||||
AddGPR(14);
|
||||
AddGPR(15);
|
||||
AddGPR(16);
|
||||
AddGPR(17);
|
||||
AddGPR(18);
|
||||
AddGPR(19);
|
||||
AddGPR(20);
|
||||
AddGPR(21);
|
||||
AddGPR(22);
|
||||
AddGPR(23);
|
||||
AddGPR(24);
|
||||
AddGPR(25);
|
||||
AddGPR(26);
|
||||
AddGPR(27);
|
||||
AddGPR(28);
|
||||
AddGPR(29);
|
||||
AddGPR(30);
|
||||
AddGPR(31);
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif TARGET_CPU_X86
|
||||
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
i386_thread_state_t *machine_state =
|
||||
reinterpret_cast<i386_thread_state_t *>(state);
|
||||
vm_address_t start_addr = machine_state->esp;
|
||||
return WriteStackFromStartAddress(start_addr, stack_location);
|
||||
}
|
||||
|
||||
u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
|
||||
i386_thread_state_t *machine_state =
|
||||
reinterpret_cast<i386_thread_state_t *>(state);
|
||||
|
||||
return machine_state->eip;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location) {
|
||||
TypedMDRVA<MDRawContextX86> context(&writer_);
|
||||
i386_thread_state_t *machine_state =
|
||||
reinterpret_cast<i386_thread_state_t *>(state);
|
||||
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
|
||||
*register_location = context.location();
|
||||
MDRawContextX86 *context_ptr = context.get();
|
||||
context_ptr->context_flags = MD_CONTEXT_X86;
|
||||
#define AddReg(a) context_ptr->a = machine_state->a
|
||||
AddReg(cs);
|
||||
AddReg(ds);
|
||||
AddReg(ss);
|
||||
AddReg(es);
|
||||
AddReg(fs);
|
||||
AddReg(gs);
|
||||
AddReg(eflags);
|
||||
|
||||
AddReg(eip);
|
||||
AddReg(eax);
|
||||
AddReg(ebx);
|
||||
AddReg(ecx);
|
||||
AddReg(edx);
|
||||
AddReg(esi);
|
||||
AddReg(edi);
|
||||
AddReg(ebp);
|
||||
AddReg(esp);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
|
||||
MDRawThread *thread) {
|
||||
breakpad_thread_state_data_t state;
|
||||
mach_msg_type_number_t state_count = sizeof(state);
|
||||
|
||||
if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
|
||||
state, &state_count) ==
|
||||
KERN_SUCCESS) {
|
||||
if (!WriteStack(state, &thread->stack))
|
||||
return false;
|
||||
|
||||
if (!WriteContext(state, &thread->thread_context))
|
||||
return false;
|
||||
|
||||
thread->thread_id = thread_id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteThreadListStream(
|
||||
MDRawDirectory *thread_list_stream) {
|
||||
TypedMDRVA<MDRawThreadList> list(&writer_);
|
||||
thread_act_port_array_t threads_for_task;
|
||||
mach_msg_type_number_t thread_count;
|
||||
int non_generator_thread_count;
|
||||
|
||||
if (task_threads(crashing_task_, &threads_for_task, &thread_count))
|
||||
return false;
|
||||
|
||||
// Don't include the generator thread
|
||||
non_generator_thread_count = thread_count - 1;
|
||||
if (!list.AllocateObjectAndArray(non_generator_thread_count,
|
||||
sizeof(MDRawThread)))
|
||||
return false;
|
||||
|
||||
thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
|
||||
thread_list_stream->location = list.location();
|
||||
|
||||
list.get()->number_of_threads = non_generator_thread_count;
|
||||
|
||||
MDRawThread thread;
|
||||
int thread_idx = 0;
|
||||
|
||||
for (unsigned int i = 0; i < thread_count; ++i) {
|
||||
memset(&thread, 0, sizeof(MDRawThread));
|
||||
|
||||
if (threads_for_task[i] != handler_thread_) {
|
||||
if (!WriteThreadStream(threads_for_task[i], &thread))
|
||||
return false;
|
||||
|
||||
list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
|
||||
TypedMDRVA<MDRawExceptionStream> exception(&writer_);
|
||||
|
||||
if (!exception.Allocate())
|
||||
return false;
|
||||
|
||||
exception_stream->stream_type = MD_EXCEPTION_STREAM;
|
||||
exception_stream->location = exception.location();
|
||||
MDRawExceptionStream *exception_ptr = exception.get();
|
||||
exception_ptr->thread_id = exception_thread_;
|
||||
|
||||
// This naming is confusing, but it is the proper translation from
|
||||
// mach naming to minidump naming.
|
||||
exception_ptr->exception_record.exception_code = exception_type_;
|
||||
exception_ptr->exception_record.exception_flags = exception_code_;
|
||||
|
||||
breakpad_thread_state_data_t state;
|
||||
mach_msg_type_number_t stateCount = sizeof(state);
|
||||
|
||||
if (thread_get_state(exception_thread_, BREAKPAD_MACHINE_THREAD_STATE, state,
|
||||
&stateCount) != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
if (!WriteContext(state, &exception_ptr->thread_context))
|
||||
return false;
|
||||
|
||||
exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteSystemInfoStream(
|
||||
MDRawDirectory *system_info_stream) {
|
||||
TypedMDRVA<MDRawSystemInfo> info(&writer_);
|
||||
|
||||
if (!info.Allocate())
|
||||
return false;
|
||||
|
||||
system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
|
||||
system_info_stream->location = info.location();
|
||||
|
||||
// CPU Information
|
||||
uint32_t cpu_type;
|
||||
size_t len = sizeof(cpu_type);
|
||||
sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
|
||||
uint32_t number_of_processors;
|
||||
len = sizeof(number_of_processors);
|
||||
sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
|
||||
MDRawSystemInfo *info_ptr = info.get();
|
||||
|
||||
switch (cpu_type) {
|
||||
case CPU_TYPE_POWERPC:
|
||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
|
||||
break;
|
||||
case CPU_TYPE_I386:
|
||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
|
||||
break;
|
||||
default:
|
||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
info_ptr->number_of_processors = number_of_processors;
|
||||
info_ptr->platform_id = MD_OS_MAC_OS_X;
|
||||
|
||||
MDLocationDescriptor build_string_loc;
|
||||
|
||||
if (!writer_.WriteString(build_string_, 0,
|
||||
&build_string_loc))
|
||||
return false;
|
||||
|
||||
info_ptr->csd_version_rva = build_string_loc.rva;
|
||||
info_ptr->major_version = os_major_version_;
|
||||
info_ptr->minor_version = os_minor_version_;
|
||||
info_ptr->build_number = os_build_number_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||
MDRawModule *module) {
|
||||
DynamicImage *image = dynamic_images_->GetImage(index);
|
||||
|
||||
if (!image)
|
||||
return false;
|
||||
|
||||
const mach_header *header = image->GetMachHeader();
|
||||
|
||||
if (!header)
|
||||
return false;
|
||||
|
||||
int cpu_type = header->cputype;
|
||||
|
||||
memset(module, 0, sizeof(MDRawModule));
|
||||
|
||||
MDLocationDescriptor string_location;
|
||||
|
||||
const char* name = image->GetFilePath();
|
||||
if (!writer_.WriteString(name, 0, &string_location))
|
||||
return false;
|
||||
|
||||
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
|
||||
module->size_of_image = image->GetVMSize();
|
||||
module->module_name_rva = string_location.rva;
|
||||
|
||||
if (!WriteCVRecord(module, cpu_type, name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int MinidumpGenerator::FindExecutableModule() {
|
||||
int index = dynamic_images_->GetExecutableImageIndex();
|
||||
|
||||
if (index >= 0) {
|
||||
return index;
|
||||
}
|
||||
|
||||
// failed - just use the first image
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||
const char *module_path) {
|
||||
TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
|
||||
|
||||
// Only return the last path component of the full module path
|
||||
char *module_name = strrchr(module_path, '/');
|
||||
|
||||
// Increment past the slash
|
||||
if (module_name)
|
||||
++module_name;
|
||||
else
|
||||
module_name = "<Unknown>";
|
||||
|
||||
size_t module_name_length = strlen(module_name);
|
||||
|
||||
if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
|
||||
return false;
|
||||
|
||||
if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
|
||||
return false;
|
||||
|
||||
module->cv_record = cv.location();
|
||||
MDCVInfoPDB70 *cv_ptr = cv.get();
|
||||
cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
||||
cv_ptr->age = 0;
|
||||
|
||||
// Get the module identifier
|
||||
FileID file_id(module_path);
|
||||
unsigned char identifier[16];
|
||||
|
||||
if (file_id.MachoIdentifier(cpu_type, identifier)) {
|
||||
cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
|
||||
(uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
|
||||
(uint32_t)identifier[3];
|
||||
cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
|
||||
cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
|
||||
cv_ptr->signature.data4[0] = identifier[8];
|
||||
cv_ptr->signature.data4[1] = identifier[9];
|
||||
cv_ptr->signature.data4[2] = identifier[10];
|
||||
cv_ptr->signature.data4[3] = identifier[11];
|
||||
cv_ptr->signature.data4[4] = identifier[12];
|
||||
cv_ptr->signature.data4[5] = identifier[13];
|
||||
cv_ptr->signature.data4[6] = identifier[14];
|
||||
cv_ptr->signature.data4[7] = identifier[15];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteModuleListStream(
|
||||
MDRawDirectory *module_list_stream) {
|
||||
TypedMDRVA<MDRawModuleList> list(&writer_);
|
||||
|
||||
if (!_dyld_present())
|
||||
return false;
|
||||
|
||||
int image_count = dynamic_images_->GetImageCount();
|
||||
|
||||
if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
|
||||
return false;
|
||||
|
||||
module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
|
||||
module_list_stream->location = list.location();
|
||||
list.get()->number_of_modules = image_count;
|
||||
|
||||
// Write out the executable module as the first one
|
||||
MDRawModule module;
|
||||
int executableIndex = FindExecutableModule();
|
||||
|
||||
if (!WriteModuleStream(executableIndex, &module)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
|
||||
int destinationIndex = 1; // Write all other modules after this one
|
||||
|
||||
for (int i = 0; i < image_count; ++i) {
|
||||
if (i != executableIndex) {
|
||||
if (!WriteModuleStream(i, &module)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
|
||||
TypedMDRVA<MDRawMiscInfo> info(&writer_);
|
||||
|
||||
if (!info.Allocate())
|
||||
return false;
|
||||
|
||||
misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
|
||||
misc_info_stream->location = info.location();
|
||||
|
||||
MDRawMiscInfo *info_ptr = info.get();
|
||||
info_ptr->size_of_info = sizeof(MDRawMiscInfo);
|
||||
info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
|
||||
MD_MISCINFO_FLAGS1_PROCESS_TIMES |
|
||||
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
|
||||
|
||||
// Process ID
|
||||
info_ptr->process_id = getpid();
|
||||
|
||||
// Times
|
||||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage) != -1) {
|
||||
// Omit the fractional time since the MDRawMiscInfo only wants seconds
|
||||
info_ptr->process_user_time = usage.ru_utime.tv_sec;
|
||||
info_ptr->process_kernel_time = usage.ru_stime.tv_sec;
|
||||
}
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
|
||||
size_t size;
|
||||
if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) {
|
||||
vm_address_t addr;
|
||||
if (vm_allocate(mach_task_self(), &addr, size, true) == KERN_SUCCESS) {
|
||||
struct kinfo_proc *proc = (struct kinfo_proc *)addr;
|
||||
if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0))
|
||||
info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec;
|
||||
vm_deallocate(mach_task_self(), addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Speed
|
||||
uint64_t speed;
|
||||
size = sizeof(speed);
|
||||
sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
|
||||
info_ptr->processor_max_mhz = speed / (1000 * 1000);
|
||||
info_ptr->processor_mhz_limit = speed / (1000 * 1000);
|
||||
size = sizeof(speed);
|
||||
sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
|
||||
info_ptr->processor_current_mhz = speed / (1000 * 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteBreakpadInfoStream(
|
||||
MDRawDirectory *breakpad_info_stream) {
|
||||
TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
|
||||
|
||||
if (!info.Allocate())
|
||||
return false;
|
||||
|
||||
breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
|
||||
breakpad_info_stream->location = info.location();
|
||||
MDRawBreakpadInfo *info_ptr = info.get();
|
||||
|
||||
if (exception_thread_ && exception_type_) {
|
||||
info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
||||
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
||||
info_ptr->dump_thread_id = handler_thread_;
|
||||
info_ptr->requesting_thread_id = exception_thread_;
|
||||
} else {
|
||||
info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
|
||||
info_ptr->dump_thread_id = handler_thread_;
|
||||
info_ptr->requesting_thread_id = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -76,7 +76,8 @@ inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
|
|||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
|
||||
void *src, size_t size) {
|
||||
const void *src,
|
||||
size_t size) {
|
||||
assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
|
||||
return writer_->Copy(position_ + sizeof(MDType) + index * size, src, size);
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ class TypedMDRVA : public UntypedMDRVA {
|
|||
// 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, void *src, size_t size);
|
||||
bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size);
|
||||
|
||||
// Write data_
|
||||
bool Flush();
|
||||
|
|
|
@ -64,6 +64,7 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
|||
minidump_write_dump_(NULL),
|
||||
installed_handler_(install_handler),
|
||||
previous_filter_(NULL),
|
||||
previous_pch_(NULL),
|
||||
handler_thread_(0),
|
||||
handler_critical_section_(),
|
||||
handler_start_semaphore_(NULL),
|
||||
|
@ -124,6 +125,8 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
|||
previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);
|
||||
#endif // _MSC_VER >= 1400
|
||||
|
||||
previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);
|
||||
|
||||
LeaveCriticalSection(&handler_stack_critical_section_);
|
||||
}
|
||||
}
|
||||
|
@ -142,6 +145,8 @@ ExceptionHandler::~ExceptionHandler() {
|
|||
_set_invalid_parameter_handler(previous_iph_);
|
||||
#endif // _MSC_VER >= 1400
|
||||
|
||||
_set_purecall_handler(previous_pch_);
|
||||
|
||||
if (handler_stack_->back() == this) {
|
||||
handler_stack_->pop_back();
|
||||
} else {
|
||||
|
@ -233,6 +238,7 @@ class AutoExceptionHandler {
|
|||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
_set_invalid_parameter_handler(handler_->previous_iph_);
|
||||
#endif // _MSC_VER >= 1400
|
||||
_set_purecall_handler(handler_->previous_pch_);
|
||||
}
|
||||
|
||||
~AutoExceptionHandler() {
|
||||
|
@ -241,6 +247,7 @@ class AutoExceptionHandler {
|
|||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
_set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter);
|
||||
#endif // _MSC_VER >= 1400
|
||||
_set_purecall_handler(ExceptionHandler::HandlePureVirtualCall);
|
||||
|
||||
EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
|
||||
--ExceptionHandler::handler_stack_index_;
|
||||
|
@ -349,6 +356,33 @@ void ExceptionHandler::HandleInvalidParameter(const wchar_t *expression,
|
|||
}
|
||||
#endif // _MSC_VER >= 1400
|
||||
|
||||
// static
|
||||
void ExceptionHandler::HandlePureVirtualCall() {
|
||||
AutoExceptionHandler auto_exception_handler;
|
||||
ExceptionHandler *current_handler = auto_exception_handler.get_handler();
|
||||
|
||||
MDRawAssertionInfo assertion;
|
||||
memset(&assertion, 0, sizeof(assertion));
|
||||
assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
|
||||
|
||||
if (!current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion)) {
|
||||
if (current_handler->previous_pch_) {
|
||||
// The handler didn't fully handle the exception. Give it to the
|
||||
// previous purecall handler.
|
||||
current_handler->previous_pch_();
|
||||
} else {
|
||||
// If there's no previous handler, return and let _purecall handle it.
|
||||
// This will just put up an assertion dialog.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The handler either took care of the invalid parameter problem itself,
|
||||
// or passed it on to another handler. "Swallow" it by exiting, paralleling
|
||||
// the behavior of "swallowing" exceptions.
|
||||
exit(0);
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpOnHandlerThread(
|
||||
EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion) {
|
||||
EnterCriticalSection(&handler_critical_section_);
|
||||
|
|
|
@ -183,6 +183,10 @@ class ExceptionHandler {
|
|||
uintptr_t reserved);
|
||||
#endif // _MSC_VER >= 1400
|
||||
|
||||
// This function will be called by the CRT when a pure virtual
|
||||
// function is called.
|
||||
static void HandlePureVirtualCall();
|
||||
|
||||
// This is called on the exception thread or on another thread that
|
||||
// the user wishes to produce a dump from. It calls
|
||||
// WriteMinidumpWithException on the handler thread, avoiding stack
|
||||
|
@ -254,6 +258,10 @@ class ExceptionHandler {
|
|||
_invalid_parameter_handler previous_iph_;
|
||||
#endif // _MSC_VER >= 1400
|
||||
|
||||
// The CRT allows you to override the default handler for pure
|
||||
// virtual function calls.
|
||||
_purecall_handler previous_pch_;
|
||||
|
||||
// The exception handler thread.
|
||||
HANDLE handler_thread_;
|
||||
|
||||
|
|
|
@ -1,58 +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.
|
||||
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include "client/windows/sender/crash_report_sender.h"
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// static
|
||||
ReportResult CrashReportSender::SendCrashReport(
|
||||
const wstring &url, const map<wstring, wstring> ¶meters,
|
||||
const wstring &dump_file_name, wstring *report_code) {
|
||||
|
||||
int http_response = 0;
|
||||
bool result = HTTPUpload::SendRequest(
|
||||
url, parameters, dump_file_name, L"upload_file_minidump", report_code,
|
||||
&http_response);
|
||||
|
||||
if (result) {
|
||||
return RESULT_SUCCEEDED;
|
||||
} else if (http_response == 400) { // TODO: update if/when the server
|
||||
// switches to a different code
|
||||
return RESULT_REJECTED;
|
||||
} else {
|
||||
return RESULT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,90 +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.
|
||||
|
||||
#ifndef CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
|
||||
#define CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
|
||||
|
||||
// CrashReportSender is a "static" class which provides an API to upload
|
||||
// crash reports via HTTP(S). A crash report is formatted as a multipart POST
|
||||
// request, which contains a set of caller-supplied string key/value pairs,
|
||||
// and a minidump file to upload.
|
||||
//
|
||||
// To use this library in your project, you will need to link against
|
||||
// wininet.lib.
|
||||
|
||||
#pragma warning( push )
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
|
||||
typedef enum {
|
||||
RESULT_FAILED = 0, // Failed to communicate with the server; try later.
|
||||
RESULT_REJECTED, // Successfully sent the crash report, but the
|
||||
// server rejected it; don't resend this report.
|
||||
RESULT_SUCCEEDED // The server accepted the crash report.
|
||||
} ReportResult;
|
||||
|
||||
class CrashReportSender {
|
||||
public:
|
||||
// Sends the specified minidump file, along with the map of
|
||||
// name value pairs, as a multipart POST request to the given URL.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. The return value indicates
|
||||
// the result of the operation (see above for possible results).
|
||||
// If report_code is non-NULL and the report is sent successfully (that is,
|
||||
// the return value is RESULT_SUCCEEDED), a code uniquely identifying the
|
||||
// report will be returned in report_code.
|
||||
// (Otherwise, report_code will be unchanged.)
|
||||
static ReportResult SendCrashReport(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const wstring &dump_file_name,
|
||||
wstring *report_code);
|
||||
|
||||
private:
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
CrashReportSender();
|
||||
explicit CrashReportSender(const CrashReportSender &);
|
||||
void operator=(const CrashReportSender &);
|
||||
~CrashReportSender();
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#pragma warning( pop )
|
||||
|
||||
#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
|
|
@ -1,923 +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.
|
||||
|
||||
// dump_syms.mm: Create a symbol file for use with minidumps
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <cxxabi.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <mach/machine.h>
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <mach-o/stab.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "dump_syms.h"
|
||||
#import "common/mac/file_id.h"
|
||||
#import "common/mac/macho_utilities.h"
|
||||
|
||||
using google_breakpad::FileID;
|
||||
|
||||
static NSString *kAddressSymbolKey = @"symbol";
|
||||
static NSString *kAddressConvertedSymbolKey = @"converted_symbol";
|
||||
static NSString *kAddressSourceLineKey = @"line";
|
||||
static NSString *kFunctionSizeKey = @"size";
|
||||
static NSString *kHeaderBaseAddressKey = @"baseAddr";
|
||||
static NSString *kHeaderSizeKey = @"size";
|
||||
static NSString *kHeaderOffsetKey = @"offset"; // Offset to the header
|
||||
static NSString *kHeaderIs64BitKey = @"is64";
|
||||
static NSString *kHeaderCPUTypeKey = @"cpuType";
|
||||
static NSString *kUnknownSymbol = @"???";
|
||||
|
||||
// The section for __TEXT, __text seems to be always 1. This is useful
|
||||
// for pruning out extraneous non-function symbols.
|
||||
static const int kTextSection = 1;
|
||||
|
||||
@interface DumpSymbols(PrivateMethods)
|
||||
- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols;
|
||||
- (void)convertSymbols;
|
||||
- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address section:(int)section;
|
||||
- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table;
|
||||
- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadSymbolInfoForArchitecture;
|
||||
- (void)generateSectionDictionary:(struct mach_header*)header;
|
||||
- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset;
|
||||
- (BOOL)loadModuleInfo;
|
||||
@end
|
||||
|
||||
@implementation DumpSymbols
|
||||
//=============================================================================
|
||||
- (NSArray *)convertCPlusPlusSymbols:(NSArray *)symbols {
|
||||
NSMutableArray *symbols_demangled = [[NSMutableArray alloc]
|
||||
initWithCapacity:[symbols count]];
|
||||
// __cxa_demangle will realloc this if needed
|
||||
char *buffer = (char *)malloc(1024);
|
||||
size_t buffer_size = 1024;
|
||||
int result;
|
||||
|
||||
NSEnumerator *enumerator = [symbols objectEnumerator];
|
||||
id symbolObject;
|
||||
while ((symbolObject = [enumerator nextObject])) {
|
||||
const char *symbol = [symbolObject UTF8String];
|
||||
buffer = abi::__cxa_demangle(symbol, buffer, &buffer_size, &result);
|
||||
if (result == 0) {
|
||||
[symbols_demangled addObject:[NSString stringWithUTF8String:buffer]];
|
||||
} else {
|
||||
// unable to demangle - use mangled name instead
|
||||
[symbols_demangled addObject:symbolObject];
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
return symbols_demangled;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)convertSymbols {
|
||||
unsigned int count = [cppAddresses_ count];
|
||||
NSMutableArray *symbols = [[NSMutableArray alloc] initWithCapacity:count];
|
||||
|
||||
// Sort addresses for processing
|
||||
NSArray *addresses = [cppAddresses_ sortedArrayUsingSelector:
|
||||
@selector(compare:)];
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
NSMutableDictionary *dict = [addresses_ objectForKey:
|
||||
[addresses objectAtIndex:i]];
|
||||
NSString *symbol = [dict objectForKey:kAddressSymbolKey];
|
||||
|
||||
// Make sure that the symbol is valid
|
||||
if ([symbol length] < 1)
|
||||
symbol = kUnknownSymbol;
|
||||
|
||||
[symbols addObject:symbol];
|
||||
}
|
||||
|
||||
// In order to deal with crashing problems in c++filt, we setup
|
||||
// a while loop to handle the case where convertCPlusPlusSymbols
|
||||
// only returns partial results.
|
||||
// We then attempt to continue from the point where c++filt failed
|
||||
// and add the partial results to the total results until we're
|
||||
// completely done.
|
||||
|
||||
unsigned int totalIndex = 0;
|
||||
unsigned int totalCount = count;
|
||||
|
||||
while (totalIndex < totalCount) {
|
||||
NSRange range = NSMakeRange(totalIndex, totalCount - totalIndex);
|
||||
NSArray *subarray = [symbols subarrayWithRange:range];
|
||||
NSArray *converted = [self convertCPlusPlusSymbols:subarray];
|
||||
unsigned int convertedCount = [converted count];
|
||||
|
||||
if (convertedCount == 0) {
|
||||
break; // we give up at this point
|
||||
}
|
||||
|
||||
for (unsigned int convertedIndex = 0;
|
||||
convertedIndex < convertedCount && totalIndex < totalCount;
|
||||
++totalIndex, ++convertedIndex) {
|
||||
NSMutableDictionary *dict = [addresses_ objectForKey:
|
||||
[addresses objectAtIndex:totalIndex]];
|
||||
NSString *symbol = [converted objectAtIndex:convertedIndex];
|
||||
|
||||
// Only add if this is a non-zero length symbol
|
||||
if ([symbol length])
|
||||
[dict setObject:symbol forKey:kAddressConvertedSymbolKey];
|
||||
}
|
||||
}
|
||||
|
||||
[symbols release];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)addFunction:(NSString *)name line:(int)line address:(uint64_t)address section:(int)section {
|
||||
NSNumber *addressNum = [NSNumber numberWithUnsignedLongLong:address];
|
||||
|
||||
if (!address)
|
||||
return;
|
||||
|
||||
// If the function starts with "_Z" or "__Z" then add it to the list of
|
||||
// addresses to run through the c++filt
|
||||
BOOL isCPP = NO;
|
||||
|
||||
if ([name hasPrefix:@"__Z"]) {
|
||||
// Remove the leading underscore
|
||||
name = [name substringFromIndex:1];
|
||||
isCPP = YES;
|
||||
} else if ([name hasPrefix:@"_Z"]) {
|
||||
isCPP = YES;
|
||||
}
|
||||
|
||||
// Filter out non-functions
|
||||
if ([name hasSuffix:@".eh"])
|
||||
return;
|
||||
|
||||
if ([name hasSuffix:@"__func__"])
|
||||
return;
|
||||
|
||||
if ([name hasSuffix:@"GCC_except_table"])
|
||||
return;
|
||||
|
||||
if (isCPP) {
|
||||
// OBJCPP_MANGLING_HACK
|
||||
// There are cases where ObjC++ mangles up an ObjC name using quasi-C++
|
||||
// mangling:
|
||||
// @implementation Foozles + (void)barzles {
|
||||
// static int Baz = 0;
|
||||
// } @end
|
||||
// gives you _ZZ18+[Foozles barzles]E3Baz
|
||||
// c++filt won't parse this properly, and will crash in certain cases.
|
||||
// Logged as radar:
|
||||
// 5129938: c++filt does not deal with ObjC++ symbols
|
||||
// If 5129938 ever gets fixed, we can remove this, but for now this prevents
|
||||
// c++filt from attempting to demangle names it doesn't know how to handle.
|
||||
// This is with c++filt 2.16
|
||||
NSCharacterSet *objcppCharSet = [NSCharacterSet characterSetWithCharactersInString:@"-+[]: "];
|
||||
NSRange emptyRange = { NSNotFound, 0 };
|
||||
NSRange objcppRange = [name rangeOfCharacterFromSet:objcppCharSet];
|
||||
isCPP = NSEqualRanges(objcppRange, emptyRange);
|
||||
}
|
||||
|
||||
if (isCPP) {
|
||||
if (!cppAddresses_)
|
||||
cppAddresses_ = [[NSMutableArray alloc] init];
|
||||
[cppAddresses_ addObject:addressNum];
|
||||
} else if ([name characterAtIndex:0] == '_') {
|
||||
// Remove the leading underscore
|
||||
name = [name substringFromIndex:1];
|
||||
}
|
||||
|
||||
// If there's already an entry for this address, check and see if we can add
|
||||
// either the symbol, or a missing line #
|
||||
NSMutableDictionary *dict = [addresses_ objectForKey:addressNum];
|
||||
|
||||
if (!dict) {
|
||||
dict = [[NSMutableDictionary alloc] init];
|
||||
[addresses_ setObject:dict forKey:addressNum];
|
||||
[dict release];
|
||||
}
|
||||
|
||||
if (name && ![dict objectForKey:kAddressSymbolKey]) {
|
||||
[dict setObject:name forKey:kAddressSymbolKey];
|
||||
|
||||
// only functions, not line number addresses
|
||||
[functionAddresses_ addObject:addressNum];
|
||||
}
|
||||
|
||||
if (line && ![dict objectForKey:kAddressSourceLineKey])
|
||||
[dict setObject:[NSNumber numberWithUnsignedInt:line]
|
||||
forKey:kAddressSourceLineKey];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)processSymbolItem:(struct nlist_64 *)list stringTable:(char *)table {
|
||||
uint32_t n_strx = list->n_un.n_strx;
|
||||
BOOL result = NO;
|
||||
|
||||
// We don't care about non-section specific information except function length
|
||||
if (list->n_sect == 0 && list->n_type != N_FUN )
|
||||
return NO;
|
||||
|
||||
if (list->n_type == N_FUN) {
|
||||
if (list->n_sect != 0) {
|
||||
// we get the function address from the first N_FUN
|
||||
lastStartAddress_ = list->n_value;
|
||||
}
|
||||
else {
|
||||
// an N_FUN from section 0 may follow the initial N_FUN
|
||||
// giving us function length information
|
||||
NSMutableDictionary *dict = [addresses_ objectForKey:
|
||||
[NSNumber numberWithUnsignedLong:lastStartAddress_]];
|
||||
|
||||
assert(dict);
|
||||
|
||||
// only set the function size the first time
|
||||
// (sometimes multiple section 0 N_FUN entries appear!)
|
||||
if (![dict objectForKey:kFunctionSizeKey]) {
|
||||
[dict setObject:[NSNumber numberWithUnsignedLongLong:list->n_value]
|
||||
forKey:kFunctionSizeKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int line = list->n_desc;
|
||||
|
||||
// We only care about line number information in __TEXT __text
|
||||
uint32_t mainSection = [[sectionNumbers_ objectForKey:@"__TEXT__text" ] unsignedLongValue];
|
||||
if(list->n_sect != mainSection) {
|
||||
line = 0;
|
||||
}
|
||||
|
||||
// Extract debugging information:
|
||||
// Doc: http://developer.apple.com/documentation/DeveloperTools/gdb/stabs/stabs_toc.html
|
||||
// Header: /usr/include/mach-o/stab.h:
|
||||
if (list->n_type == N_SO) {
|
||||
NSString *src = [NSString stringWithUTF8String:&table[n_strx]];
|
||||
NSString *ext = [src pathExtension];
|
||||
NSNumber *address = [NSNumber numberWithUnsignedLongLong:list->n_value];
|
||||
|
||||
// TODO(waylonis):Ensure that we get the full path for the source file
|
||||
// from the first N_SO record
|
||||
// If there is an extension, we'll consider it source code
|
||||
if ([ext length]) {
|
||||
if (!sources_)
|
||||
sources_ = [[NSMutableDictionary alloc] init];
|
||||
// Save the source associated with an address
|
||||
[sources_ setObject:src forKey:address];
|
||||
|
||||
result = YES;
|
||||
}
|
||||
} else if (list->n_type == N_FUN) {
|
||||
NSString *fn = [NSString stringWithUTF8String:&table[n_strx]];
|
||||
NSRange range = [fn rangeOfString:@":" options:NSBackwardsSearch];
|
||||
|
||||
if (![fn length] || !range.length)
|
||||
return NO;
|
||||
|
||||
// The function has a ":" followed by some stuff
|
||||
fn = [fn substringToIndex:range.location];
|
||||
[self addFunction:fn line:line address:list->n_value section:list->n_sect ];
|
||||
|
||||
result = YES;
|
||||
} else if (list->n_type == N_SLINE && list->n_sect == mainSection ) {
|
||||
[self addFunction:nil line:line address:list->n_value section:list->n_sect ];
|
||||
result = YES;
|
||||
} else if (((list->n_type & N_TYPE) == N_SECT) && !(list->n_type & N_STAB)) {
|
||||
// Regular symbols or ones that are external
|
||||
NSString *fn = [NSString stringWithUTF8String:&table[n_strx]];
|
||||
|
||||
[self addFunction:fn line:0 address:list->n_value section:list->n_sect ];
|
||||
result = YES;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define SwapLongLongIfNeeded(a) (swap ? NXSwapLongLong(a) : (a))
|
||||
#define SwapLongIfNeeded(a) (swap ? NXSwapLong(a) : (a))
|
||||
#define SwapIntIfNeeded(a) (swap ? NXSwapInt(a) : (a))
|
||||
#define SwapShortIfNeeded(a) (swap ? NXSwapShort(a) : (a))
|
||||
//=============================================================================
|
||||
- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset {
|
||||
struct mach_header *header = (struct mach_header *)((uint32_t)base + offset);
|
||||
BOOL swap = (header->magic == MH_CIGAM);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
struct load_command *cmd =
|
||||
(struct load_command *)((uint32_t)header + sizeof(struct mach_header));
|
||||
uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB);
|
||||
BOOL result = NO;
|
||||
|
||||
if (!addresses_)
|
||||
addresses_ = [[NSMutableDictionary alloc] init];
|
||||
|
||||
for (uint32_t i = 0; cmd && (i < count); ++i) {
|
||||
if (cmd->cmd == symbolTableCommand) {
|
||||
struct symtab_command *symtab = (struct symtab_command *)cmd;
|
||||
uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms);
|
||||
uint32_t symoff = SwapLongIfNeeded(symtab->symoff);
|
||||
uint32_t stroff = SwapLongIfNeeded(symtab->stroff);
|
||||
struct nlist *list = (struct nlist *)((uint32_t)base + symoff + offset);
|
||||
char *strtab = ((char *)header + stroff);
|
||||
|
||||
// Process each command, looking for debugging stuff
|
||||
for (uint32_t j = 0; j < ncmds; ++j, ++list) {
|
||||
// Fill in an nlist_64 structure and process with that
|
||||
struct nlist_64 nlist64;
|
||||
nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx);
|
||||
nlist64.n_type = list->n_type;
|
||||
nlist64.n_sect = list->n_sect;
|
||||
nlist64.n_desc = SwapShortIfNeeded(list->n_desc);
|
||||
nlist64.n_value = (uint64_t)SwapLongIfNeeded(list->n_value);
|
||||
|
||||
if ([self processSymbolItem:&nlist64 stringTable:strtab])
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize);
|
||||
cmd = (struct load_command *)((uint32_t)cmd + cmdSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadSymbolInfo64:(void *)base offset:(uint32_t)offset {
|
||||
struct mach_header_64 *header = (struct mach_header_64 *)
|
||||
((uint32_t)base + offset);
|
||||
BOOL swap = (header->magic == MH_CIGAM_64);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
struct load_command *cmd =
|
||||
(struct load_command *)((uint32_t)header + sizeof(struct mach_header));
|
||||
uint32_t symbolTableCommand = SwapLongIfNeeded(LC_SYMTAB);
|
||||
BOOL result = NO;
|
||||
|
||||
for (uint32_t i = 0; cmd && (i < count); i++) {
|
||||
if (cmd->cmd == symbolTableCommand) {
|
||||
struct symtab_command *symtab = (struct symtab_command *)cmd;
|
||||
uint32_t ncmds = SwapLongIfNeeded(symtab->nsyms);
|
||||
uint32_t symoff = SwapLongIfNeeded(symtab->symoff);
|
||||
uint32_t stroff = SwapLongIfNeeded(symtab->stroff);
|
||||
struct nlist_64 *list = (struct nlist_64 *)((uint32_t)base + symoff);
|
||||
char *strtab = ((char *)header + stroff);
|
||||
|
||||
// Process each command, looking for debugging stuff
|
||||
for (uint32_t j = 0; j < ncmds; ++j, ++list) {
|
||||
if (!(list->n_type & (N_STAB | N_TYPE)))
|
||||
continue;
|
||||
|
||||
// Fill in an nlist_64 structure and process with that
|
||||
struct nlist_64 nlist64;
|
||||
nlist64.n_un.n_strx = SwapLongIfNeeded(list->n_un.n_strx);
|
||||
nlist64.n_type = list->n_type;
|
||||
nlist64.n_sect = list->n_sect;
|
||||
nlist64.n_desc = SwapShortIfNeeded(list->n_desc);
|
||||
nlist64.n_value = SwapLongLongIfNeeded(list->n_value);
|
||||
|
||||
if ([self processSymbolItem:&nlist64 stringTable:strtab])
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize);
|
||||
cmd = (struct load_command *)((uint32_t)cmd + cmdSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadSymbolInfoForArchitecture {
|
||||
NSMutableData *data = [[NSMutableData alloc]
|
||||
initWithContentsOfMappedFile:sourcePath_];
|
||||
NSDictionary *headerInfo = [headers_ objectForKey:architecture_];
|
||||
void *base = [data mutableBytes];
|
||||
uint32_t offset =
|
||||
[[headerInfo objectForKey:kHeaderOffsetKey] unsignedLongValue];
|
||||
BOOL is64 = [[headerInfo objectForKey:kHeaderIs64BitKey] boolValue];
|
||||
BOOL result = is64 ? [self loadSymbolInfo64:base offset:offset] :
|
||||
[self loadSymbolInfo:base offset:offset];
|
||||
|
||||
[data release];
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// build a dictionary of section numbers keyed off a string
|
||||
// which is the concatenation of the segment name and the section name
|
||||
- (void)generateSectionDictionary:(struct mach_header*)header {
|
||||
BOOL swap = (header->magic == MH_CIGAM);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
struct load_command *cmd =
|
||||
(struct load_command *)((uint32_t)header + sizeof(struct mach_header));
|
||||
uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT);
|
||||
uint32_t sectionNumber = 1; // section numbers are counted from 1
|
||||
|
||||
if (!sectionNumbers_)
|
||||
sectionNumbers_ = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// loop through every segment command, then through every section
|
||||
// contained inside each of them
|
||||
for (uint32_t i = 0; cmd && (i < count); ++i) {
|
||||
if (cmd->cmd == segmentCommand) {
|
||||
struct segment_command *seg = (struct segment_command *)cmd;
|
||||
section *sect = (section *)((uint32_t)cmd + sizeof(segment_command));
|
||||
uint32_t nsects = SwapLongIfNeeded(seg->nsects);
|
||||
|
||||
for (uint32_t j = 0; j < nsects; ++j) {
|
||||
//printf("%d: %s %s\n", sectionNumber, seg->segname, sect->sectname );
|
||||
NSString *segSectName = [NSString stringWithFormat:@"%s%s",
|
||||
seg->segname, sect->sectname ];
|
||||
|
||||
[sectionNumbers_ setValue:[NSNumber numberWithUnsignedLong:sectionNumber]
|
||||
forKey:segSectName ];
|
||||
|
||||
++sect;
|
||||
++sectionNumber;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize);
|
||||
cmd = (struct load_command *)((uint32_t)cmd + cmdSize);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadHeader:(void *)base offset:(uint32_t)offset {
|
||||
struct mach_header *header = (struct mach_header *)((uint32_t)base + offset);
|
||||
BOOL swap = (header->magic == MH_CIGAM);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
struct load_command *cmd =
|
||||
(struct load_command *)((uint32_t)header + sizeof(struct mach_header));
|
||||
uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT);
|
||||
|
||||
[self generateSectionDictionary:header];
|
||||
|
||||
for (uint32_t i = 0; cmd && (i < count); ++i) {
|
||||
if (cmd->cmd == segmentCommand) {
|
||||
struct segment_command *seg = (struct segment_command *)cmd;
|
||||
|
||||
if (!strcmp(seg->segname, "__TEXT")) {
|
||||
uint32_t addr = SwapLongIfNeeded(seg->vmaddr);
|
||||
uint32_t size = SwapLongIfNeeded(seg->vmsize);
|
||||
cpu_type_t cpu = SwapIntIfNeeded(header->cputype);
|
||||
NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86" : @"ppc";
|
||||
|
||||
[headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithUnsignedLongLong:(uint64_t)addr],
|
||||
kHeaderBaseAddressKey,
|
||||
[NSNumber numberWithUnsignedLongLong:(uint64_t)size], kHeaderSizeKey,
|
||||
[NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey,
|
||||
[NSNumber numberWithBool:NO], kHeaderIs64BitKey,
|
||||
[NSNumber numberWithUnsignedLong:cpu], kHeaderCPUTypeKey,
|
||||
nil] forKey:cpuStr];
|
||||
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize);
|
||||
cmd = (struct load_command *)((uint32_t)cmd + cmdSize);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadHeader64:(void *)base offset:(uint32_t)offset {
|
||||
struct mach_header_64 *header =
|
||||
(struct mach_header_64 *)((uint32_t)base + offset);
|
||||
BOOL swap = (header->magic == MH_CIGAM_64);
|
||||
uint32_t count = SwapLongIfNeeded(header->ncmds);
|
||||
struct load_command *cmd =
|
||||
(struct load_command *)((uint32_t)header + sizeof(struct mach_header_64));
|
||||
|
||||
for (uint32_t i = 0; cmd && (i < count); ++i) {
|
||||
uint32_t segmentCommand = SwapLongIfNeeded(LC_SEGMENT_64);
|
||||
if (cmd->cmd == segmentCommand) {
|
||||
struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
|
||||
if (!strcmp(seg->segname, "__TEXT")) {
|
||||
uint64_t addr = SwapLongLongIfNeeded(seg->vmaddr);
|
||||
uint64_t size = SwapLongLongIfNeeded(seg->vmsize);
|
||||
cpu_type_t cpu = SwapIntIfNeeded(header->cputype);
|
||||
cpu &= (~CPU_ARCH_ABI64);
|
||||
NSString *cpuStr = (cpu == CPU_TYPE_I386) ? @"x86_64" : @"ppc64";
|
||||
|
||||
[headers_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithUnsignedLongLong:addr], kHeaderBaseAddressKey,
|
||||
[NSNumber numberWithUnsignedLongLong:size], kHeaderSizeKey,
|
||||
[NSNumber numberWithUnsignedLong:offset], kHeaderOffsetKey,
|
||||
[NSNumber numberWithBool:YES], kHeaderIs64BitKey,
|
||||
[NSNumber numberWithUnsignedLong:cpu], kHeaderCPUTypeKey,
|
||||
nil] forKey:cpuStr];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cmdSize = SwapLongIfNeeded(cmd->cmdsize);
|
||||
cmd = (struct load_command *)((uint32_t)cmd + cmdSize);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)loadModuleInfo {
|
||||
uint64_t result = 0;
|
||||
NSMutableData *data = [[NSMutableData alloc]
|
||||
initWithContentsOfMappedFile:sourcePath_];
|
||||
void *bytes = [data mutableBytes];
|
||||
struct fat_header *fat = (struct fat_header *)bytes;
|
||||
|
||||
if (!fat) {
|
||||
[data release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gather some information based on the header
|
||||
BOOL isFat = fat->magic == FAT_MAGIC || fat->magic == FAT_CIGAM;
|
||||
BOOL is64 = fat->magic == MH_MAGIC_64 || fat->magic == MH_CIGAM_64;
|
||||
BOOL is32 = fat->magic == MH_MAGIC || fat->magic == MH_CIGAM;
|
||||
BOOL swap = fat->magic == FAT_CIGAM || fat->magic == MH_CIGAM_64 ||
|
||||
fat->magic == MH_CIGAM;
|
||||
|
||||
if (!is64 && !is32 && !isFat) {
|
||||
[data release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load any available architectures and save the information
|
||||
headers_ = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (isFat) {
|
||||
struct fat_arch *archs =
|
||||
(struct fat_arch *)((uint32_t)fat + sizeof(struct fat_header));
|
||||
uint32_t count = SwapLongIfNeeded(fat->nfat_arch);
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
archs[i].cputype = SwapIntIfNeeded(archs[i].cputype);
|
||||
archs[i].cpusubtype = SwapIntIfNeeded(archs[i].cpusubtype);
|
||||
archs[i].offset = SwapLongIfNeeded(archs[i].offset);
|
||||
archs[i].size = SwapLongIfNeeded(archs[i].size);
|
||||
archs[i].align = SwapLongIfNeeded(archs[i].align);
|
||||
|
||||
if (archs[i].cputype & CPU_ARCH_ABI64)
|
||||
result = [self loadHeader64:bytes offset:archs[i].offset];
|
||||
else
|
||||
result = [self loadHeader:bytes offset:archs[i].offset];
|
||||
}
|
||||
} else if (is32) {
|
||||
result = [self loadHeader:bytes offset:0];
|
||||
} else {
|
||||
result = [self loadHeader64:bytes offset:0];
|
||||
}
|
||||
|
||||
[data release];
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static BOOL WriteFormat(int fd, const char *fmt, ...) {
|
||||
va_list list;
|
||||
char buffer[4096];
|
||||
ssize_t expected, written;
|
||||
|
||||
va_start(list, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, list);
|
||||
expected = strlen(buffer);
|
||||
written = write(fd, buffer, strlen(buffer));
|
||||
va_end(list);
|
||||
|
||||
return expected == written;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)outputSymbolFile:(int)fd {
|
||||
// Get the baseAddress for this architecture
|
||||
NSDictionary *archDict = [headers_ objectForKey:architecture_];
|
||||
NSNumber *baseAddressNum = [archDict objectForKey:kHeaderBaseAddressKey];
|
||||
uint64_t baseAddress =
|
||||
baseAddressNum ? [baseAddressNum unsignedLongLongValue] : 0;
|
||||
NSNumber *moduleSizeNum = [archDict objectForKey:kHeaderSizeKey];
|
||||
uint64_t moduleSize =
|
||||
moduleSizeNum ? [moduleSizeNum unsignedLongLongValue] : 0;
|
||||
|
||||
// UUID
|
||||
FileID file_id([sourcePath_ fileSystemRepresentation]);
|
||||
unsigned char identifier[16];
|
||||
char identifierStr[40];
|
||||
const char *moduleName = [[sourcePath_ lastPathComponent] UTF8String];
|
||||
int cpu_type = [[archDict objectForKey:kHeaderCPUTypeKey] unsignedLongValue];
|
||||
if (file_id.MachoIdentifier(cpu_type, identifier)) {
|
||||
FileID::ConvertIdentifierToString(identifier, identifierStr,
|
||||
sizeof(identifierStr));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Unable to calculate UUID of mach-o binary!\n");
|
||||
return NO;
|
||||
}
|
||||
|
||||
// keep track exclusively of function addresses
|
||||
// for sanity checking function lengths
|
||||
functionAddresses_ = [[NSMutableSet alloc] init];
|
||||
|
||||
// Gather the information
|
||||
[self loadSymbolInfoForArchitecture];
|
||||
[self convertSymbols];
|
||||
|
||||
NSArray *sortedAddresses = [[addresses_ allKeys]
|
||||
sortedArrayUsingSelector:@selector(compare:)];
|
||||
|
||||
NSArray *sortedFunctionAddresses = [[functionAddresses_ allObjects]
|
||||
sortedArrayUsingSelector:@selector(compare:)];
|
||||
|
||||
// position ourselves at the 2nd function
|
||||
unsigned int funcIndex = 1;
|
||||
|
||||
// Remove the dashes from the string
|
||||
NSMutableString *compactedStr =
|
||||
[NSMutableString stringWithCString:identifierStr encoding:NSASCIIStringEncoding];
|
||||
[compactedStr replaceOccurrencesOfString:@"-" withString:@"" options:0
|
||||
range:NSMakeRange(0, [compactedStr length])];
|
||||
|
||||
if (!WriteFormat(fd, "MODULE mac %s %s0 %s\n", [architecture_ UTF8String],
|
||||
[compactedStr UTF8String], moduleName)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Sources ordered by address
|
||||
NSArray *sources = [[sources_ allKeys]
|
||||
sortedArrayUsingSelector:@selector(compare:)];
|
||||
unsigned int sourceCount = [sources count];
|
||||
for (unsigned int i = 0; i < sourceCount; ++i) {
|
||||
NSString *file = [sources_ objectForKey:[sources objectAtIndex:i]];
|
||||
if (!WriteFormat(fd, "FILE %d %s\n", i + 1, [file UTF8String]))
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Symbols
|
||||
char terminatingChar = '\n';
|
||||
uint32_t fileIdx = 0, nextFileIdx = 0;
|
||||
uint64_t nextSourceFileAddress = 0;
|
||||
NSNumber *nextAddress;
|
||||
uint64_t nextAddressVal;
|
||||
unsigned int addressCount = [sortedAddresses count];
|
||||
|
||||
for (unsigned int i = 0; i < addressCount; ++i) {
|
||||
NSNumber *address = [sortedAddresses objectAtIndex:i];
|
||||
uint64_t addressVal = [address unsignedLongLongValue] - baseAddress;
|
||||
|
||||
// Get the next address to calculate the length
|
||||
if (i + 1 < addressCount) {
|
||||
nextAddress = [sortedAddresses objectAtIndex:i + 1];
|
||||
nextAddressVal = [nextAddress unsignedLongLongValue] - baseAddress;
|
||||
} else {
|
||||
nextAddressVal = baseAddress + moduleSize;
|
||||
// The symbol reader doesn't want a trailing newline
|
||||
terminatingChar = '\0';
|
||||
}
|
||||
|
||||
NSDictionary *dict = [addresses_ objectForKey:address];
|
||||
NSNumber *line = [dict objectForKey:kAddressSourceLineKey];
|
||||
NSString *symbol = [dict objectForKey:kAddressConvertedSymbolKey];
|
||||
|
||||
if (!symbol)
|
||||
symbol = [dict objectForKey:kAddressSymbolKey];
|
||||
|
||||
// sanity check the function length by making sure it doesn't
|
||||
// run beyond the next function entry
|
||||
uint64_t nextFunctionAddress = 0;
|
||||
if (symbol && funcIndex < [sortedFunctionAddresses count]) {
|
||||
nextFunctionAddress = [[sortedFunctionAddresses objectAtIndex:funcIndex]
|
||||
unsignedLongLongValue] - baseAddress;
|
||||
++funcIndex;
|
||||
}
|
||||
|
||||
// Skip some symbols
|
||||
if ([symbol hasPrefix:@"vtable for"])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"__static_initialization_and_destruction_0"])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"_GLOBAL__I__"])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"__func__."])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"__gnu"])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"typeinfo "])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"EH_frame"])
|
||||
continue;
|
||||
|
||||
if ([symbol hasPrefix:@"GCC_except_table"])
|
||||
continue;
|
||||
|
||||
// Find the source file (if any) that contains this address
|
||||
while (sourceCount && (addressVal >= nextSourceFileAddress)) {
|
||||
fileIdx = nextFileIdx;
|
||||
|
||||
if (nextFileIdx < sourceCount) {
|
||||
NSNumber *addr = [sources objectAtIndex:nextFileIdx];
|
||||
++nextFileIdx;
|
||||
nextSourceFileAddress = [addr unsignedLongLongValue] - baseAddress;
|
||||
} else {
|
||||
nextSourceFileAddress = baseAddress + moduleSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NSNumber *functionLength = [dict objectForKey:kFunctionSizeKey];
|
||||
|
||||
if (line) {
|
||||
if (symbol && functionLength) {
|
||||
uint64_t functionLengthVal = [functionLength unsignedLongLongValue];
|
||||
|
||||
// sanity check to make sure the length we were told does not exceed
|
||||
// the space between this function and the next
|
||||
if (nextFunctionAddress != 0) {
|
||||
uint64_t functionLengthVal2 = nextFunctionAddress - addressVal;
|
||||
|
||||
if(functionLengthVal > functionLengthVal2 ) {
|
||||
functionLengthVal = functionLengthVal2;
|
||||
}
|
||||
}
|
||||
|
||||
// Function
|
||||
if (!WriteFormat(fd, "FUNC %llx %llx 0 %s\n", addressVal,
|
||||
functionLengthVal, [symbol UTF8String]))
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Source line
|
||||
uint64_t length = nextAddressVal - addressVal;
|
||||
if (!WriteFormat(fd, "%llx %llx %d %d\n", addressVal, length,
|
||||
[line unsignedIntValue], fileIdx))
|
||||
return NO;
|
||||
} else {
|
||||
// PUBLIC <address> <stack-size> <name>
|
||||
if (!WriteFormat(fd, "PUBLIC %llx 0 %s\n", addressVal,
|
||||
[symbol UTF8String]))
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (id)initWithContentsOfFile:(NSString *)path {
|
||||
if ((self = [super init])) {
|
||||
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
[self autorelease];
|
||||
return nil;
|
||||
}
|
||||
|
||||
sourcePath_ = [path copy];
|
||||
|
||||
if (![self loadModuleInfo]) {
|
||||
[self autorelease];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// If there's more than one, use the native one
|
||||
if ([headers_ count] > 1) {
|
||||
const NXArchInfo *localArchInfo = NXGetLocalArchInfo();
|
||||
|
||||
if (localArchInfo) {
|
||||
cpu_type_t cpu = localArchInfo->cputype;
|
||||
NSString *arch;
|
||||
|
||||
if (cpu & CPU_ARCH_ABI64)
|
||||
arch = ((cpu & ~CPU_ARCH_ABI64) == CPU_TYPE_X86) ?
|
||||
@"x86_64" : @"ppc64";
|
||||
else
|
||||
arch = (cpu == CPU_TYPE_X86) ? @"x86" : @"ppc";
|
||||
|
||||
[self setArchitecture:arch];
|
||||
}
|
||||
} else {
|
||||
// Specify the default architecture
|
||||
[self setArchitecture:[[headers_ allKeys] objectAtIndex:0]];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSArray *)availableArchitectures {
|
||||
return [headers_ allKeys];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)dealloc {
|
||||
[sourcePath_ release];
|
||||
[architecture_ release];
|
||||
[addresses_ release];
|
||||
[functionAddresses_ release];
|
||||
[sources_ release];
|
||||
[headers_ release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)setArchitecture:(NSString *)architecture {
|
||||
NSString *normalized = [architecture lowercaseString];
|
||||
BOOL isValid = NO;
|
||||
|
||||
if ([normalized isEqualToString:@"ppc"]) {
|
||||
isValid = YES;
|
||||
}
|
||||
else if ([normalized isEqualToString:@"i386"]) {
|
||||
normalized = @"x86";
|
||||
isValid = YES;
|
||||
}
|
||||
else if ([normalized isEqualToString:@"x86"]) {
|
||||
isValid = YES;
|
||||
}
|
||||
else if ([normalized isEqualToString:@"ppc64"]) {
|
||||
isValid = YES;
|
||||
}
|
||||
else if ([normalized isEqualToString:@"x86_64"]) {
|
||||
isValid = YES;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
if (![headers_ objectForKey:normalized])
|
||||
return NO;
|
||||
|
||||
[architecture_ autorelease];
|
||||
architecture_ = [architecture copy];
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSString *)architecture {
|
||||
return architecture_;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)writeSymbolFile:(NSString *)destinationPath {
|
||||
const char *dest = [destinationPath fileSystemRepresentation];
|
||||
int fd;
|
||||
|
||||
if ([[destinationPath substringToIndex:1] isEqualToString:@"-"])
|
||||
fd = STDOUT_FILENO;
|
||||
else
|
||||
fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
|
||||
if (fd == -1)
|
||||
return NO;
|
||||
|
||||
BOOL result = [self outputSymbolFile:fd];
|
||||
|
||||
close(fd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
|
@ -867,7 +867,7 @@ typedef struct {
|
|||
|
||||
typedef union {
|
||||
struct {
|
||||
u_int32_t vendor_id[3]; /* cpuid 0: eax, ebx, ecx */
|
||||
u_int32_t vendor_id[3]; /* cpuid 0: ebx, edx, ecx */
|
||||
u_int32_t version_information; /* cpuid 1: eax */
|
||||
u_int32_t feature_information; /* cpuid 1: edx */
|
||||
u_int32_t amd_extended_cpu_features; /* cpuid 0x80000001, ebx */
|
||||
|
@ -1046,13 +1046,17 @@ typedef struct {
|
|||
u_int32_t type;
|
||||
} MDRawAssertionInfo;
|
||||
|
||||
/* For (MDRawAssertionInfo).info: */
|
||||
/* For (MDRawAssertionInfo).type: */
|
||||
typedef enum {
|
||||
MD_ASSERTION_INFO_TYPE_UNKNOWN = 0,
|
||||
|
||||
/* Used for assertions that would be raised by the MSVC CRT but are
|
||||
* directed to an invalid parameter handler instead. */
|
||||
MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER
|
||||
MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER,
|
||||
|
||||
/* Used for assertions that would be raised by the MSVC CRT but are
|
||||
* directed to a pure virtual call handler instead. */
|
||||
MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL
|
||||
} MDAssertionInfoData;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
|
|
@ -63,13 +63,15 @@ class MinidumpProcessor {
|
|||
|
||||
// Populates the cpu_* fields of the |info| parameter with textual
|
||||
// representations of the CPU type that the minidump in |dump| was
|
||||
// produced on.
|
||||
static void GetCPUInfo(Minidump *dump, SystemInfo *info);
|
||||
// produced on. Returns false if this information is not available in
|
||||
// the minidump.
|
||||
static bool GetCPUInfo(Minidump *dump, SystemInfo *info);
|
||||
|
||||
// Populates the os_* fields of the |info| parameter with textual
|
||||
// representations of the operating system that the minidump in |dump|
|
||||
// was produced on.
|
||||
static void GetOSInfo(Minidump *dump, SystemInfo *info);
|
||||
// was produced on. Returns false if this information is not available in
|
||||
// the minidump.
|
||||
static bool GetOSInfo(Minidump *dump, SystemInfo *info);
|
||||
|
||||
// Returns a textual representation of the reason that a crash occurred,
|
||||
// if the minidump in dump was produced as a result of a crash. Returns
|
||||
|
|
|
@ -1,86 +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.
|
||||
|
||||
// address_map-inl.h: Address map implementation.
|
||||
//
|
||||
// See address_map.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_ADDRESS_MAP_INL_H__
|
||||
#define PROCESSOR_ADDRESS_MAP_INL_H__
|
||||
|
||||
#include "processor/address_map.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool AddressMap<AddressType, EntryType>::Store(const AddressType &address,
|
||||
const EntryType &entry) {
|
||||
// Ensure that the specified address doesn't conflict with something already
|
||||
// in the map.
|
||||
if (map_.find(address) != map_.end())
|
||||
return false;
|
||||
|
||||
map_.insert(MapValue(address, entry));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool AddressMap<AddressType, EntryType>::Retrieve(
|
||||
const AddressType &address,
|
||||
EntryType *entry, AddressType *entry_address) const {
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
// upper_bound gives the first element whose key is greater than address,
|
||||
// but we want the first element whose key is less than or equal to address.
|
||||
// Decrement the iterator to get there, but not if the upper_bound already
|
||||
// points to the beginning of the map - in that case, address is lower than
|
||||
// the lowest stored key, so return false.
|
||||
MapConstIterator iterator = map_.upper_bound(address);
|
||||
if (iterator == map_.begin())
|
||||
return false;
|
||||
--iterator;
|
||||
|
||||
*entry = iterator->second;
|
||||
if (entry_address)
|
||||
*entry_address = iterator->first;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
void AddressMap<AddressType, EntryType>::Clear() {
|
||||
map_.clear();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_ADDRESS_MAP_INL_H__
|
|
@ -1,80 +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.
|
||||
|
||||
// address_map.h: Address maps.
|
||||
//
|
||||
// An address map contains a set of objects keyed by address. Objects are
|
||||
// retrieved from the map by returning the object with the highest key less
|
||||
// than or equal to the lookup key.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_ADDRESS_MAP_H__
|
||||
#define PROCESSOR_ADDRESS_MAP_H__
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
class AddressMap {
|
||||
public:
|
||||
AddressMap() : map_() {}
|
||||
|
||||
// Inserts an entry into the map. Returns false without storing the entry
|
||||
// if an entry is already stored in the map at the same address as specified
|
||||
// by the address argument.
|
||||
bool Store(const AddressType &address, const EntryType &entry);
|
||||
|
||||
// Locates the entry stored at the highest address less than or equal to
|
||||
// the address argument. If there is no such range, or if there is a
|
||||
// parameter error, returns false. The entry is returned in entry. If
|
||||
// entry_address is not NULL, it will be set to the address that the entry
|
||||
// was stored at.
|
||||
bool Retrieve(const AddressType &address,
|
||||
EntryType *entry, AddressType *entry_address) const;
|
||||
|
||||
// Empties the address map, restoring it to the same state as when it was
|
||||
// initially created.
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
// Convenience types.
|
||||
typedef std::map<AddressType, EntryType> AddressToEntryMap;
|
||||
typedef typename AddressToEntryMap::const_iterator MapConstIterator;
|
||||
typedef typename AddressToEntryMap::value_type MapValue;
|
||||
|
||||
// Maps the address of each entry to an EntryType.
|
||||
AddressToEntryMap map_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_ADDRESS_MAP_H__
|
||||
|
|
@ -107,7 +107,6 @@ static bool DoAddressMapTest() {
|
|||
ASSERT_EQ(entry->id(), 1);
|
||||
ASSERT_EQ(address, 10);
|
||||
ASSERT_TRUE(test_map.Retrieve(11, &entry, &address));
|
||||
ASSERT_FALSE(test_map.Retrieve(11, NULL, &address)); // parameter error
|
||||
ASSERT_TRUE(test_map.Retrieve(11, &entry, NULL)); // NULL ok here
|
||||
|
||||
// Add some more elements.
|
||||
|
|
|
@ -1,112 +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.
|
||||
|
||||
// basic_code_modules.cc: Contains all of the CodeModule objects that
|
||||
// were loaded into a single process.
|
||||
//
|
||||
// See basic_code_modules.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "processor/basic_code_modules.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
BasicCodeModules::BasicCodeModules(const CodeModules *that)
|
||||
: main_address_(0),
|
||||
map_(new RangeMap<u_int64_t, linked_ptr<const CodeModule> >()) {
|
||||
assert(that);
|
||||
|
||||
const CodeModule *main_module = that->GetMainModule();
|
||||
if (main_module)
|
||||
main_address_ = main_module->base_address();
|
||||
|
||||
unsigned int count = that->module_count();
|
||||
for (unsigned int module_sequence = 0;
|
||||
module_sequence < count;
|
||||
++module_sequence) {
|
||||
// Make a copy of the module and insert it into the map. Use
|
||||
// GetModuleAtIndex because ordering is unimportant when slurping the
|
||||
// entire list, and GetModuleAtIndex may be faster than
|
||||
// GetModuleAtSequence.
|
||||
const CodeModule *module = that->GetModuleAtIndex(module_sequence)->Copy();
|
||||
map_->StoreRange(module->base_address(), module->size(),
|
||||
linked_ptr<const CodeModule>(module));
|
||||
}
|
||||
}
|
||||
|
||||
BasicCodeModules::~BasicCodeModules() {
|
||||
delete map_;
|
||||
}
|
||||
|
||||
unsigned int BasicCodeModules::module_count() const {
|
||||
return map_->GetCount();
|
||||
}
|
||||
|
||||
const CodeModule* BasicCodeModules::GetModuleForAddress(
|
||||
u_int64_t address) const {
|
||||
linked_ptr<const CodeModule> module;
|
||||
if (!map_->RetrieveRange(address, &module, NULL, NULL))
|
||||
return NULL;
|
||||
|
||||
return module.get();
|
||||
}
|
||||
|
||||
const CodeModule* BasicCodeModules::GetMainModule() const {
|
||||
return GetModuleForAddress(main_address_);
|
||||
}
|
||||
|
||||
const CodeModule* BasicCodeModules::GetModuleAtSequence(
|
||||
unsigned int sequence) const {
|
||||
linked_ptr<const CodeModule> module;
|
||||
if (!map_->RetrieveRangeAtIndex(sequence, &module, NULL, NULL))
|
||||
return NULL;
|
||||
|
||||
return module.get();
|
||||
}
|
||||
|
||||
const CodeModule* BasicCodeModules::GetModuleAtIndex(
|
||||
unsigned int index) const {
|
||||
// This class stores everything in a RangeMap, without any more-efficient
|
||||
// way to walk the list of CodeModule objects. Implement GetModuleAtIndex
|
||||
// using GetModuleAtSequence, which meets all of the requirements, and
|
||||
// in addition, guarantees ordering.
|
||||
return GetModuleAtSequence(index);
|
||||
}
|
||||
|
||||
const CodeModules* BasicCodeModules::Copy() const {
|
||||
return new BasicCodeModules(this);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,561 +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.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "processor/address_map-inl.h"
|
||||
#include "processor/contained_range_map-inl.h"
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
|
||||
using std::map;
|
||||
using std::vector;
|
||||
using std::make_pair;
|
||||
using __gnu_cxx::hash;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct BasicSourceLineResolver::Line {
|
||||
Line(MemAddr addr, MemAddr code_size, int file_id, int source_line)
|
||||
: address(addr)
|
||||
, size(code_size)
|
||||
, source_file_id(file_id)
|
||||
, line(source_line) { }
|
||||
|
||||
MemAddr address;
|
||||
MemAddr size;
|
||||
int source_file_id;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct BasicSourceLineResolver::Function {
|
||||
Function(const string &function_name,
|
||||
MemAddr function_address,
|
||||
MemAddr code_size,
|
||||
int set_parameter_size)
|
||||
: name(function_name), address(function_address), size(code_size),
|
||||
parameter_size(set_parameter_size) { }
|
||||
|
||||
string name;
|
||||
MemAddr address;
|
||||
MemAddr size;
|
||||
|
||||
// The size of parameters passed to this function on the stack.
|
||||
int parameter_size;
|
||||
|
||||
RangeMap< MemAddr, linked_ptr<Line> > lines;
|
||||
};
|
||||
|
||||
struct BasicSourceLineResolver::PublicSymbol {
|
||||
PublicSymbol(const string& set_name,
|
||||
MemAddr set_address,
|
||||
int set_parameter_size)
|
||||
: name(set_name),
|
||||
address(set_address),
|
||||
parameter_size(set_parameter_size) {}
|
||||
|
||||
string name;
|
||||
MemAddr address;
|
||||
|
||||
// If the public symbol is used as a function entry point, parameter_size
|
||||
// is set to the size of the parameters passed to the funciton on the
|
||||
// stack, if known.
|
||||
int parameter_size;
|
||||
};
|
||||
|
||||
class BasicSourceLineResolver::Module {
|
||||
public:
|
||||
Module(const string &name) : name_(name) { }
|
||||
|
||||
// Loads the given map file, returning true on success.
|
||||
bool LoadMap(const string &map_file);
|
||||
|
||||
// Looks up the given relative address, and fills the StackFrame struct
|
||||
// with the result. Additional debugging information, if available, is
|
||||
// returned. If no additional information is available, returns NULL.
|
||||
// A NULL return value is not an error. The caller takes ownership of
|
||||
// any returned StackFrameInfo object.
|
||||
StackFrameInfo* LookupAddress(StackFrame *frame) const;
|
||||
|
||||
private:
|
||||
friend class BasicSourceLineResolver;
|
||||
typedef hash_map<int, string> FileMap;
|
||||
|
||||
// The types for stack_info_. This is equivalent to MS DIA's
|
||||
// StackFrameTypeEnum. Each identifies a different type of frame
|
||||
// information, although all are represented in the symbol file in the
|
||||
// same format. These are used as indices to the stack_info_ array.
|
||||
enum StackInfoTypes {
|
||||
STACK_INFO_FPO = 0,
|
||||
STACK_INFO_TRAP, // not used here
|
||||
STACK_INFO_TSS, // not used here
|
||||
STACK_INFO_STANDARD,
|
||||
STACK_INFO_FRAME_DATA,
|
||||
STACK_INFO_LAST, // must be the last sequentially-numbered item
|
||||
STACK_INFO_UNKNOWN = -1
|
||||
};
|
||||
|
||||
// Splits line into at most max_tokens space-separated tokens, placing
|
||||
// them in the tokens vector. line is a 0-terminated string that
|
||||
// optionally ends with a newline character or combination, which will
|
||||
// be removed. line must not contain any embedded '\n' or '\r' characters.
|
||||
// If more tokens than max_tokens are present, the final token is placed
|
||||
// into the vector without splitting it up at all. This modifies line as
|
||||
// a side effect. Returns true if exactly max_tokens tokens are returned,
|
||||
// and false if fewer are returned. This is not considered a failure of
|
||||
// Tokenize, but may be treated as a failure if the caller expects an
|
||||
// exact, as opposed to maximum, number of tokens.
|
||||
static bool Tokenize(char *line, int max_tokens, vector<char*> *tokens);
|
||||
|
||||
// Parses a file declaration
|
||||
void ParseFile(char *file_line);
|
||||
|
||||
// Parses a function declaration, returning a new Function object.
|
||||
Function* ParseFunction(char *function_line);
|
||||
|
||||
// Parses a line declaration, returning a new Line object.
|
||||
Line* ParseLine(char *line_line);
|
||||
|
||||
// Parses a PUBLIC symbol declaration, storing it in public_symbols_.
|
||||
// Returns false if an error occurs.
|
||||
bool ParsePublicSymbol(char *public_line);
|
||||
|
||||
// Parses a stack frame info declaration, storing it in stack_info_.
|
||||
bool ParseStackInfo(char *stack_info_line);
|
||||
|
||||
string name_;
|
||||
FileMap files_;
|
||||
RangeMap< MemAddr, linked_ptr<Function> > functions_;
|
||||
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
|
||||
|
||||
// Each element in the array is a ContainedRangeMap for a type listed in
|
||||
// StackInfoTypes. These are split by type because there may be overlaps
|
||||
// between maps of different types, but some information is only available
|
||||
// as certain types.
|
||||
ContainedRangeMap< MemAddr, linked_ptr<StackFrameInfo> >
|
||||
stack_info_[STACK_INFO_LAST];
|
||||
};
|
||||
|
||||
BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) {
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::~BasicSourceLineResolver() {
|
||||
ModuleMap::iterator it;
|
||||
for (it = modules_->begin(); it != modules_->end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
delete modules_;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::LoadModule(const string &module_name,
|
||||
const string &map_file) {
|
||||
// Make sure we don't already have a module with the given name.
|
||||
if (modules_->find(module_name) != modules_->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Module *module = new Module(module_name);
|
||||
if (!module->LoadMap(map_file)) {
|
||||
delete module;
|
||||
return false;
|
||||
}
|
||||
|
||||
modules_->insert(make_pair(module_name, module));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::HasModule(const string &module_name) const {
|
||||
return modules_->find(module_name) != modules_->end();
|
||||
}
|
||||
|
||||
StackFrameInfo* BasicSourceLineResolver::FillSourceLineInfo(
|
||||
StackFrame *frame) const {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
return it->second->LookupAddress(frame);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) {
|
||||
FILE *f = fopen(map_file.c_str(), "r");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(mmentovai): this might not be large enough to handle really long
|
||||
// lines, which might be present for FUNC lines of highly-templatized
|
||||
// code.
|
||||
char buffer[8192];
|
||||
linked_ptr<Function> cur_func;
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), f)) {
|
||||
if (strncmp(buffer, "FILE ", 5) == 0) {
|
||||
ParseFile(buffer);
|
||||
} else if (strncmp(buffer, "STACK ", 6) == 0) {
|
||||
if (!ParseStackInfo(buffer)) {
|
||||
return false;
|
||||
}
|
||||
} else if (strncmp(buffer, "FUNC ", 5) == 0) {
|
||||
cur_func.reset(ParseFunction(buffer));
|
||||
if (!cur_func.get()) {
|
||||
return false;
|
||||
}
|
||||
// StoreRange will fail if the function has an invalid address or size.
|
||||
// We'll silently ignore this, the function and any corresponding lines
|
||||
// will be destroyed when cur_func is released.
|
||||
functions_.StoreRange(cur_func->address, cur_func->size, cur_func);
|
||||
} else if (strncmp(buffer, "PUBLIC ", 7) == 0) {
|
||||
// Clear cur_func: public symbols don't contain line number information.
|
||||
cur_func.reset();
|
||||
|
||||
if (!ParsePublicSymbol(buffer)) {
|
||||
return false;
|
||||
}
|
||||
} else if (strncmp(buffer, "MODULE ", 7) == 0) {
|
||||
// Ignore these. They're not of any use to BasicSourceLineResolver,
|
||||
// which is fed modules by a SymbolSupplier. These lines are present to
|
||||
// aid other tools in properly placing symbol files so that they can
|
||||
// be accessed by a SymbolSupplier.
|
||||
//
|
||||
// MODULE <guid> <age> <filename>
|
||||
} else {
|
||||
if (!cur_func.get()) {
|
||||
return false;
|
||||
}
|
||||
Line *line = ParseLine(buffer);
|
||||
if (!line) {
|
||||
return false;
|
||||
}
|
||||
cur_func->lines.StoreRange(line->address, line->size,
|
||||
linked_ptr<Line>(line));
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
|
||||
StackFrame *frame) const {
|
||||
MemAddr address = frame->instruction - frame->module->base_address();
|
||||
|
||||
linked_ptr<StackFrameInfo> retrieved_info;
|
||||
// Check for debugging info first, before any possible early returns.
|
||||
//
|
||||
// We only know about STACK_INFO_FRAME_DATA and STACK_INFO_FPO. Prefer
|
||||
// them in this order. STACK_INFO_FRAME_DATA is the newer type that
|
||||
// includes its own program string. STACK_INFO_FPO is the older type
|
||||
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
|
||||
if (!stack_info_[STACK_INFO_FRAME_DATA].RetrieveRange(address,
|
||||
&retrieved_info)) {
|
||||
stack_info_[STACK_INFO_FPO].RetrieveRange(address, &retrieved_info);
|
||||
}
|
||||
|
||||
scoped_ptr<StackFrameInfo> frame_info;
|
||||
if (retrieved_info.get()) {
|
||||
frame_info.reset(new StackFrameInfo());
|
||||
frame_info->CopyFrom(*retrieved_info.get());
|
||||
}
|
||||
|
||||
// First, look for a matching FUNC range. Use RetrieveNearestRange instead
|
||||
// of RetrieveRange so that the nearest function can be compared to the
|
||||
// nearest PUBLIC symbol if the address does not lie within the function.
|
||||
// Having access to the highest function below address, even when address
|
||||
// is outside of the function, is useful: if the function is higher than
|
||||
// the nearest PUBLIC symbol, then it means that the PUBLIC symbols is not
|
||||
// valid for the address, and no function information should be filled in.
|
||||
// Using RetrieveNearestRange instead of RetrieveRange means that we need
|
||||
// to verify that address is within the range before using a FUNC.
|
||||
//
|
||||
// If no FUNC containing the address is found, look for the nearest PUBLIC
|
||||
// symbol, being careful not to use a public symbol at a lower address than
|
||||
// the nearest FUNC.
|
||||
int parameter_size = 0;
|
||||
linked_ptr<Function> func;
|
||||
linked_ptr<PublicSymbol> public_symbol;
|
||||
MemAddr function_base;
|
||||
MemAddr function_size;
|
||||
MemAddr public_address;
|
||||
if (functions_.RetrieveNearestRange(address, &func,
|
||||
&function_base, &function_size) &&
|
||||
address >= function_base && address < function_base + function_size) {
|
||||
parameter_size = func->parameter_size;
|
||||
|
||||
frame->function_name = func->name;
|
||||
frame->function_base = frame->module->base_address() + function_base;
|
||||
|
||||
linked_ptr<Line> line;
|
||||
MemAddr line_base;
|
||||
if (func->lines.RetrieveRange(address, &line, &line_base, NULL)) {
|
||||
FileMap::const_iterator it = files_.find(line->source_file_id);
|
||||
if (it != files_.end()) {
|
||||
frame->source_file_name = files_.find(line->source_file_id)->second;
|
||||
}
|
||||
frame->source_line = line->line;
|
||||
frame->source_line_base = frame->module->base_address() + line_base;
|
||||
}
|
||||
} else if (public_symbols_.Retrieve(address,
|
||||
&public_symbol, &public_address) &&
|
||||
(!func.get() || public_address > function_base + function_size)) {
|
||||
parameter_size = public_symbol->parameter_size;
|
||||
|
||||
frame->function_name = public_symbol->name;
|
||||
frame->function_base = frame->module->base_address() + public_address;
|
||||
} else {
|
||||
// No FUNC or PUBLIC data available.
|
||||
return frame_info.release();
|
||||
}
|
||||
|
||||
if (!frame_info.get()) {
|
||||
// Even without a relevant STACK line, many functions contain information
|
||||
// about how much space their parameters consume on the stack. Prefer
|
||||
// the STACK stuff (above), but if it's not present, take the
|
||||
// information from the FUNC or PUBLIC line.
|
||||
frame_info.reset(new StackFrameInfo());
|
||||
frame_info->parameter_size = parameter_size;
|
||||
frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE;
|
||||
}
|
||||
|
||||
return frame_info.release();
|
||||
}
|
||||
|
||||
// static
|
||||
bool BasicSourceLineResolver::Module::Tokenize(char *line, int max_tokens,
|
||||
vector<char*> *tokens) {
|
||||
tokens->clear();
|
||||
tokens->reserve(max_tokens);
|
||||
|
||||
int remaining = max_tokens;
|
||||
|
||||
// Split tokens on the space character. Look for newlines too to
|
||||
// strip them out before exhausting max_tokens.
|
||||
char *save_ptr;
|
||||
char *token = strtok_r(line, " \r\n", &save_ptr);
|
||||
while (token && --remaining > 0) {
|
||||
tokens->push_back(token);
|
||||
if (remaining > 1)
|
||||
token = strtok_r(NULL, " \r\n", &save_ptr);
|
||||
}
|
||||
|
||||
// If there's anything left, just add it as a single token.
|
||||
if (!remaining > 0) {
|
||||
if ((token = strtok_r(NULL, "\r\n", &save_ptr))) {
|
||||
tokens->push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
return tokens->size() == static_cast<unsigned int>(max_tokens);
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::Module::ParseFile(char *file_line) {
|
||||
// FILE <id> <filename>
|
||||
file_line += 5; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(file_line, 2, &tokens)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = atoi(tokens[0]);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *filename = tokens[1];
|
||||
if (filename) {
|
||||
files_.insert(make_pair(index, string(filename)));
|
||||
}
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::Function*
|
||||
BasicSourceLineResolver::Module::ParseFunction(char *function_line) {
|
||||
// FUNC <address> <size> <stack_param_size> <name>
|
||||
function_line += 5; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(function_line, 4, &tokens)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u_int64_t address = strtoull(tokens[0], NULL, 16);
|
||||
u_int64_t size = strtoull(tokens[1], NULL, 16);
|
||||
int stack_param_size = strtoull(tokens[2], NULL, 16);
|
||||
char *name = tokens[3];
|
||||
|
||||
return new Function(name, address, size, stack_param_size);
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::Line* BasicSourceLineResolver::Module::ParseLine(
|
||||
char *line_line) {
|
||||
// <address> <line number> <source file id>
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(line_line, 4, &tokens)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u_int64_t address = strtoull(tokens[0], NULL, 16);
|
||||
u_int64_t size = strtoull(tokens[1], NULL, 16);
|
||||
int line_number = atoi(tokens[2]);
|
||||
int source_file = atoi(tokens[3]);
|
||||
if (line_number <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new Line(address, size, source_file, line_number);
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
|
||||
// PUBLIC <address> <stack_param_size> <name>
|
||||
|
||||
// Skip "PUBLIC " prefix.
|
||||
public_line += 7;
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(public_line, 3, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u_int64_t address = strtoull(tokens[0], NULL, 16);
|
||||
int stack_param_size = strtoull(tokens[1], NULL, 16);
|
||||
char *name = tokens[2];
|
||||
|
||||
// A few public symbols show up with an address of 0. This has been seen
|
||||
// in the dumped output of ntdll.pdb for symbols such as _CIlog, _CIpow,
|
||||
// RtlDescribeChunkLZNT1, and RtlReserveChunkLZNT1. They would conflict
|
||||
// with one another if they were allowed into the public_symbols_ map,
|
||||
// but since the address is obviously invalid, gracefully accept them
|
||||
// as input without putting them into the map.
|
||||
if (address == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
linked_ptr<PublicSymbol> symbol(new PublicSymbol(name, address,
|
||||
stack_param_size));
|
||||
return public_symbols_.Store(address, symbol);
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
|
||||
// STACK WIN <type> <rva> <code_size> <prolog_size> <epliog_size>
|
||||
// <parameter_size> <saved_register_size> <local_size> <max_stack_size>
|
||||
// <has_program_string> <program_string_OR_allocates_base_pointer>
|
||||
//
|
||||
// If has_program_string is 1, the rest of the line is a program string.
|
||||
// Otherwise, the final token tells whether the stack info indicates that
|
||||
// a base pointer has been allocated.
|
||||
//
|
||||
// Expect has_program_string to be 1 when type is STACK_INFO_FRAME_DATA and
|
||||
// 0 when type is STACK_INFO_FPO, but don't enforce this.
|
||||
|
||||
// Skip "STACK " prefix.
|
||||
stack_info_line += 6;
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(stack_info_line, 12, &tokens))
|
||||
return false;
|
||||
|
||||
// Only MSVC stack frame info is understood for now.
|
||||
const char *platform = tokens[0];
|
||||
if (strcmp(platform, "WIN") != 0)
|
||||
return false;
|
||||
|
||||
int type = strtol(tokens[1], NULL, 16);
|
||||
if (type < 0 || type > STACK_INFO_LAST - 1)
|
||||
return false;
|
||||
|
||||
u_int64_t rva = strtoull(tokens[2], NULL, 16);
|
||||
u_int64_t code_size = strtoull(tokens[3], NULL, 16);
|
||||
u_int32_t prolog_size = strtoul(tokens[4], NULL, 16);
|
||||
u_int32_t epilog_size = strtoul(tokens[5], NULL, 16);
|
||||
u_int32_t parameter_size = strtoul(tokens[6], NULL, 16);
|
||||
u_int32_t saved_register_size = strtoul(tokens[7], NULL, 16);
|
||||
u_int32_t local_size = strtoul(tokens[8], NULL, 16);
|
||||
u_int32_t max_stack_size = strtoul(tokens[9], NULL, 16);
|
||||
int has_program_string = strtoul(tokens[10], NULL, 16);
|
||||
|
||||
const char *program_string = "";
|
||||
int allocates_base_pointer = 0;
|
||||
if (has_program_string) {
|
||||
program_string = tokens[11];
|
||||
} else {
|
||||
allocates_base_pointer = strtoul(tokens[11], NULL, 16);
|
||||
}
|
||||
|
||||
// TODO(mmentovai): I wanted to use StoreRange's return value as this
|
||||
// method's return value, but MSVC infrequently outputs stack info that
|
||||
// violates the containment rules. This happens with a section of code
|
||||
// in strncpy_s in test_app.cc (testdata/minidump2). There, problem looks
|
||||
// like this:
|
||||
// STACK WIN 4 4242 1a a 0 ... (STACK WIN 4 base size prolog 0 ...)
|
||||
// STACK WIN 4 4243 2e 9 0 ...
|
||||
// ContainedRangeMap treats these two blocks as conflicting. In reality,
|
||||
// when the prolog lengths are taken into account, the actual code of
|
||||
// these blocks doesn't conflict. However, we can't take the prolog lengths
|
||||
// into account directly here because we'd wind up with a different set
|
||||
// of range conflicts when MSVC outputs stack info like this:
|
||||
// STACK WIN 4 1040 73 33 0 ...
|
||||
// STACK WIN 4 105a 59 19 0 ...
|
||||
// because in both of these entries, the beginning of the code after the
|
||||
// prolog is at 0x1073, and the last byte of contained code is at 0x10b2.
|
||||
// Perhaps we could get away with storing ranges by rva + prolog_size
|
||||
// if ContainedRangeMap were modified to allow replacement of
|
||||
// already-stored values.
|
||||
|
||||
linked_ptr<StackFrameInfo> stack_frame_info(
|
||||
new StackFrameInfo(prolog_size,
|
||||
epilog_size,
|
||||
parameter_size,
|
||||
saved_register_size,
|
||||
local_size,
|
||||
max_stack_size,
|
||||
allocates_base_pointer,
|
||||
program_string));
|
||||
stack_info_[type].StoreRange(rva, code_size, stack_frame_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t BasicSourceLineResolver::HashString::operator()(const string &s) const {
|
||||
return hash<const char*>()(s.c_str());
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,172 +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.
|
||||
|
||||
// contained_range_map-inl.h: Hierarchically-organized range map implementation.
|
||||
//
|
||||
// See contained_range_map.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
|
||||
#define PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
|
||||
|
||||
|
||||
#include "processor/contained_range_map.h"
|
||||
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
ContainedRangeMap<AddressType, EntryType>::~ContainedRangeMap() {
|
||||
// Clear frees the children pointed to by the map, and frees the map itself.
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool ContainedRangeMap<AddressType, EntryType>::StoreRange(
|
||||
const AddressType &base, const AddressType &size, const EntryType &entry) {
|
||||
AddressType high = base + size - 1;
|
||||
|
||||
// Check for undersize or overflow.
|
||||
if (size <= 0 || high < base)
|
||||
return false;
|
||||
|
||||
if (!map_)
|
||||
map_ = new AddressToRangeMap();
|
||||
|
||||
MapIterator iterator_base = map_->lower_bound(base);
|
||||
MapIterator iterator_high = map_->lower_bound(high);
|
||||
MapConstIterator iterator_end = map_->end();
|
||||
|
||||
if (iterator_base == iterator_high && iterator_base != iterator_end &&
|
||||
base >= iterator_base->second->base_) {
|
||||
// The new range is entirely within an existing child range.
|
||||
|
||||
// If the new range's geometry is exactly equal to an existing child
|
||||
// range's, it violates the containment rules, and an attempt to store
|
||||
// it must fail. iterator_base->first contains the key, which was the
|
||||
// containing child's high address.
|
||||
if (iterator_base->second->base_ == base && iterator_base->first == high)
|
||||
return false;
|
||||
|
||||
// Pass the new range on to the child to attempt to store.
|
||||
return iterator_base->second->StoreRange(base, size, entry);
|
||||
}
|
||||
|
||||
// iterator_high might refer to an irrelevant range: one whose base address
|
||||
// is higher than the new range's high address. Set contains_high to true
|
||||
// only if iterator_high refers to a range that is at least partially
|
||||
// within the new range.
|
||||
bool contains_high = iterator_high != iterator_end &&
|
||||
high >= iterator_high->second->base_;
|
||||
|
||||
// If the new range encompasses any existing child ranges, it must do so
|
||||
// fully. Partial containment isn't allowed.
|
||||
if ((iterator_base != iterator_end && base > iterator_base->second->base_) ||
|
||||
(contains_high && high < iterator_high->first)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// When copying and erasing contained ranges, the "end" iterator needs to
|
||||
// point one past the last item of the range to copy. If contains_high is
|
||||
// false, the iterator's already in the right place; the increment is safe
|
||||
// because contains_high can't be true if iterator_high == iterator_end.
|
||||
if (contains_high)
|
||||
++iterator_high;
|
||||
|
||||
// Optimization: if the iterators are equal, no child ranges would be
|
||||
// moved. Create the new child range with a NULL map to conserve space
|
||||
// in leaf nodes, of which there will be many.
|
||||
AddressToRangeMap *child_map = NULL;
|
||||
|
||||
if (iterator_base != iterator_high) {
|
||||
// The children of this range that are contained by the new range must
|
||||
// be transferred over to the new range. Create the new child range map
|
||||
// and copy the pointers to range maps it should contain into it.
|
||||
child_map = new AddressToRangeMap(iterator_base, iterator_high);
|
||||
|
||||
// Remove the copied child pointers from this range's map of children.
|
||||
map_->erase(iterator_base, iterator_high);
|
||||
}
|
||||
|
||||
// Store the new range in the map by its high address. Any children that
|
||||
// the new child range contains were formerly children of this range but
|
||||
// are now this range's grandchildren. Ownership of these is transferred
|
||||
// to the new child range.
|
||||
map_->insert(MapValue(high,
|
||||
new ContainedRangeMap(base, entry, child_map)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool ContainedRangeMap<AddressType, EntryType>::RetrieveRange(
|
||||
const AddressType &address, EntryType *entry) const {
|
||||
if (!entry || !map_)
|
||||
return false;
|
||||
|
||||
// Get an iterator to the child range whose high address is equal to or
|
||||
// greater than the supplied address. If the supplied address is higher
|
||||
// than all of the high addresses in the range, then this range does not
|
||||
// contain a child at address, so return false. If the supplied address
|
||||
// is lower than the base address of the child range, then it is not within
|
||||
// the child range, so return false.
|
||||
MapConstIterator iterator = map_->lower_bound(address);
|
||||
if (iterator == map_->end() || address < iterator->second->base_)
|
||||
return false;
|
||||
|
||||
// The child in iterator->second contains the specified address. Find out
|
||||
// if it has a more-specific descendant that also contains it. If it does,
|
||||
// it will set |entry| appropriately. If not, set |entry| to the child.
|
||||
if (!iterator->second->RetrieveRange(address, entry))
|
||||
*entry = iterator->second->entry_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
void ContainedRangeMap<AddressType, EntryType>::Clear() {
|
||||
if (map_) {
|
||||
MapConstIterator end = map_->end();
|
||||
for (MapConstIterator child = map_->begin(); child != end; ++child)
|
||||
delete child->second;
|
||||
|
||||
delete map_;
|
||||
map_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
|
|
@ -1,146 +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.
|
||||
|
||||
// contained_range_map.h: Hierarchically-organized range maps.
|
||||
//
|
||||
// A contained range map is similar to a standard range map, except it allows
|
||||
// objects to be organized hierarchically. A contained range map allows
|
||||
// objects to contain other objects. It is not sensitive to the order that
|
||||
// objects are added to the map: larger, more general, containing objects
|
||||
// may be added either before or after smaller, more specific, contained
|
||||
// ones.
|
||||
//
|
||||
// Contained range maps guarantee that each object may only contain smaller
|
||||
// objects than itself, and that a parent object may only contain child
|
||||
// objects located entirely within the parent's address space. Attempts
|
||||
// to introduce objects (via StoreRange) that violate these rules will fail.
|
||||
// Retrieval (via RetrieveRange) always returns the most specific (smallest)
|
||||
// object that contains the address being queried. Note that while it is
|
||||
// not possible to insert two objects into a map that have exactly the same
|
||||
// geometry (base address and size), it is possible to completely mask a
|
||||
// larger object by inserting smaller objects that entirely fill the larger
|
||||
// object's address space.
|
||||
//
|
||||
// Internally, contained range maps are implemented as a tree. Each tree
|
||||
// node except for the root node describes an object in the map. Each node
|
||||
// maintains its list of children in a map similar to a standard range map,
|
||||
// keyed by the highest address that each child occupies. Each node's
|
||||
// children occupy address ranges entirely within the node. The root node
|
||||
// is the only node directly accessible to the user, and represents the
|
||||
// entire address space.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_CONTAINED_RANGE_MAP_H__
|
||||
#define PROCESSOR_CONTAINED_RANGE_MAP_H__
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
class ContainedRangeMap {
|
||||
public:
|
||||
// The default constructor creates a ContainedRangeMap with no geometry
|
||||
// and no entry, and as such is only suitable for the root node of a
|
||||
// ContainedRangeMap tree.
|
||||
ContainedRangeMap() : base_(), entry_(), map_(NULL) {}
|
||||
|
||||
~ContainedRangeMap();
|
||||
|
||||
// Inserts a range into the map. If the new range is encompassed by
|
||||
// an existing child range, the new range is passed into the child range's
|
||||
// StoreRange method. If the new range encompasses any existing child
|
||||
// ranges, those child ranges are moved to the new range, becoming
|
||||
// grandchildren of this ContainedRangeMap. Returns false for a
|
||||
// parameter error, or if the ContainedRangeMap hierarchy guarantees
|
||||
// would be violated.
|
||||
bool StoreRange(const AddressType &base,
|
||||
const AddressType &size,
|
||||
const EntryType &entry);
|
||||
|
||||
// Retrieves the most specific (smallest) descendant range encompassing
|
||||
// the specified address. This method will only return entries held by
|
||||
// child ranges, and not the entry contained by |this|. This is necessary
|
||||
// to support a sparsely-populated root range. If no descendant range
|
||||
// encompasses the address, or if there is a parameter error, returns
|
||||
// false.
|
||||
bool RetrieveRange(const AddressType &address, EntryType *entry) const;
|
||||
|
||||
// Removes all children. Note that Clear only removes descendants,
|
||||
// leaving the node on which it is called intact. Because the only
|
||||
// meaningful things contained by a root node are descendants, this
|
||||
// is sufficient to restore an entire ContainedRangeMap to its initial
|
||||
// empty state when called on the root node.
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
// AddressToRangeMap stores pointers. This makes reparenting simpler in
|
||||
// StoreRange, because it doesn't need to copy entire objects.
|
||||
typedef std::map<AddressType, ContainedRangeMap *> AddressToRangeMap;
|
||||
typedef typename AddressToRangeMap::const_iterator MapConstIterator;
|
||||
typedef typename AddressToRangeMap::iterator MapIterator;
|
||||
typedef typename AddressToRangeMap::value_type MapValue;
|
||||
|
||||
// Creates a new ContainedRangeMap with the specified base address, entry,
|
||||
// and initial child map, which may be NULL. This is only used internally
|
||||
// by ContainedRangeMap when it creates a new child.
|
||||
ContainedRangeMap(const AddressType &base, const EntryType &entry,
|
||||
AddressToRangeMap *map)
|
||||
: base_(base), entry_(entry), map_(map) {}
|
||||
|
||||
// The base address of this range. The high address does not need to
|
||||
// be stored, because it is used as the key to an object in its parent's
|
||||
// map, and all ContainedRangeMaps except for the root range are contained
|
||||
// within maps. The root range does not actually contain an entry, so its
|
||||
// base_ field is meaningless, and the fact that it has no parent and thus
|
||||
// no key is unimportant. For this reason, the base_ field should only be
|
||||
// is accessed on child ContainedRangeMap objects, and never on |this|.
|
||||
const AddressType base_;
|
||||
|
||||
// The entry corresponding to this range. The root range does not
|
||||
// actually contain an entry, so its entry_ field is meaningless. For
|
||||
// this reason, the entry_ field should only be accessed on child
|
||||
// ContainedRangeMap objects, and never on |this|.
|
||||
const EntryType entry_;
|
||||
|
||||
// The map containing child ranges, keyed by each child range's high
|
||||
// address. This is a pointer to avoid allocating map structures for
|
||||
// leaf nodes, where they are not needed.
|
||||
AddressToRangeMap *map_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_CONTAINED_RANGE_MAP_H__
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -33,6 +33,7 @@
|
|||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
||||
|
@ -48,19 +49,23 @@ MinidumpProcessor::~MinidumpProcessor() {
|
|||
|
||||
MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
||||
const string &minidump_file, ProcessState *process_state) {
|
||||
BPLOG(INFO) << "Processing minidump in file " << minidump_file;
|
||||
|
||||
Minidump dump(minidump_file);
|
||||
if (!dump.Read()) {
|
||||
BPLOG(ERROR) << "Minidump " << minidump_file << " could not be read";
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
process_state->Clear();
|
||||
|
||||
const MDRawHeader *header = dump.header();
|
||||
BPLOG_IF(ERROR, !header) << "Minidump " << minidump_file << " has no header";
|
||||
assert(header);
|
||||
process_state->time_date_stamp_ = header->time_date_stamp;
|
||||
|
||||
GetCPUInfo(&dump, &process_state->system_info_);
|
||||
GetOSInfo(&dump, &process_state->system_info_);
|
||||
bool has_cpu_info = GetCPUInfo(&dump, &process_state->system_info_);
|
||||
bool has_os_info = GetOSInfo(&dump, &process_state->system_info_);
|
||||
|
||||
u_int32_t dump_thread_id = 0;
|
||||
bool has_dump_thread = false;
|
||||
|
@ -93,24 +98,45 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
|||
|
||||
MinidumpThreadList *threads = dump.GetThreadList();
|
||||
if (!threads) {
|
||||
BPLOG(ERROR) << "Minidump " << minidump_file << " has no thread list";
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
BPLOG(INFO) << "Minidump " << minidump_file << " has " <<
|
||||
(has_cpu_info ? "" : "no ") << "CPU info, " <<
|
||||
(has_os_info ? "" : "no ") << "OS info, " <<
|
||||
(breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
|
||||
(exception != NULL ? "" : "no ") << "exception, " <<
|
||||
(module_list != NULL ? "" : "no ") << "module list, " <<
|
||||
(threads != NULL ? "" : "no ") << "thread list, " <<
|
||||
(has_dump_thread ? "" : "no ") << "dump thread, and " <<
|
||||
(has_requesting_thread ? "" : "no ") << "requesting thread";
|
||||
|
||||
bool found_requesting_thread = false;
|
||||
unsigned int thread_count = threads->thread_count();
|
||||
for (unsigned int thread_index = 0;
|
||||
thread_index < thread_count;
|
||||
++thread_index) {
|
||||
char thread_string_buffer[64];
|
||||
snprintf(thread_string_buffer, sizeof(thread_string_buffer), "%d/%d",
|
||||
thread_index, thread_count);
|
||||
string thread_string = minidump_file + ":" + thread_string_buffer;
|
||||
|
||||
MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
|
||||
if (!thread) {
|
||||
BPLOG(ERROR) << "Could not get thread for " << thread_string;
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
u_int32_t thread_id;
|
||||
if (!thread->GetThreadID(&thread_id)) {
|
||||
BPLOG(ERROR) << "Could not get thread ID for " << thread_string;
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
thread_string += " id " + HexString(thread_id);
|
||||
BPLOG(INFO) << "Looking at thread " << thread_string;
|
||||
|
||||
// If this thread is the thread that produced the minidump, don't process
|
||||
// it. Because of the problems associated with a thread producing a
|
||||
// dump of itself (when both its context and its stack are in flux),
|
||||
|
@ -124,6 +150,7 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
|||
if (has_requesting_thread && thread_id == requesting_thread_id) {
|
||||
if (found_requesting_thread) {
|
||||
// There can't be more than one requesting thread.
|
||||
BPLOG(ERROR) << "Duplicate requesting thread: " << thread_string;
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
|
@ -150,6 +177,7 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
|||
|
||||
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
||||
if (!thread_memory) {
|
||||
BPLOG(ERROR) << "No memory region for " << thread_string;
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
|
@ -169,11 +197,14 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
|||
supplier_,
|
||||
resolver_));
|
||||
if (!stackwalker.get()) {
|
||||
BPLOG(ERROR) << "No stackwalker for " << thread_string;
|
||||
return PROCESS_ERROR;
|
||||
}
|
||||
|
||||
scoped_ptr<CallStack> stack(new CallStack());
|
||||
if (!stackwalker->Walk(stack.get())) {
|
||||
BPLOG(INFO) << "Processing interrupted by stackwalker (missing " <<
|
||||
"symbols?) at " << thread_string;
|
||||
return PROCESS_INTERRUPTED;
|
||||
}
|
||||
process_state->threads_.push_back(stack.release());
|
||||
|
@ -182,9 +213,13 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
|
|||
// If a requesting thread was indicated, it must be present.
|
||||
if (has_requesting_thread && !found_requesting_thread) {
|
||||
// Don't mark as an error, but invalidate the requesting thread
|
||||
BPLOG(ERROR) << "Minidump indicated requesting thread " <<
|
||||
HexString(requesting_thread_id) << ", not found in " <<
|
||||
minidump_file;
|
||||
process_state->requesting_thread_ = -1;
|
||||
}
|
||||
|
||||
BPLOG(INFO) << "Processed " << minidump_file;
|
||||
return PROCESS_OK;
|
||||
}
|
||||
|
||||
|
@ -204,7 +239,7 @@ static const MDRawSystemInfo* GetSystemInfo(Minidump *dump,
|
|||
}
|
||||
|
||||
// static
|
||||
void MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
||||
bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
||||
assert(dump);
|
||||
assert(info);
|
||||
|
||||
|
@ -214,7 +249,7 @@ void MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
|||
MinidumpSystemInfo *system_info;
|
||||
const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info);
|
||||
if (!raw_system_info)
|
||||
return;
|
||||
return false;
|
||||
|
||||
switch (raw_system_info->processor_architecture) {
|
||||
case MD_CPU_ARCHITECTURE_X86: {
|
||||
|
@ -248,10 +283,12 @@ void MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
|
||||
bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
|
||||
assert(dump);
|
||||
assert(info);
|
||||
|
||||
|
@ -262,7 +299,7 @@ void MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
|
|||
MinidumpSystemInfo *system_info;
|
||||
const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info);
|
||||
if (!raw_system_info)
|
||||
return;
|
||||
return false;
|
||||
|
||||
info->os_short = system_info->GetOS();
|
||||
|
||||
|
@ -309,6 +346,8 @@ void MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) {
|
|||
info->os_version.append(" ");
|
||||
info->os_version.append(*csd_version);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -375,6 +414,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -403,6 +443,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -429,12 +470,14 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -471,6 +514,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
reason.append("EXC_PPC_ALTIVECASSIST");
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -503,12 +547,14 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -529,6 +575,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -542,6 +589,7 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -556,12 +604,14 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
break;
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reason.append(flags_string);
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -668,7 +718,16 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
|||
case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK:
|
||||
reason = "EXCEPTION_POSSIBLE_DEADLOCK";
|
||||
break;
|
||||
default:
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
BPLOG(INFO) << "Unknown exception reason " << reason;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -255,7 +255,8 @@ static void PrintModules(const CodeModules *modules) {
|
|||
// PrintModulesMachineReadable outputs a list of loaded modules,
|
||||
// one per line, in the following machine-readable pipe-delimited
|
||||
// text format:
|
||||
// Module|{Module Filename}|{Version}|{Base Address}|{Max Address}|{Main}
|
||||
// Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}|
|
||||
// {Base Address}|{Max Address}|{Main}
|
||||
static void PrintModulesMachineReadable(const CodeModules *modules) {
|
||||
if (!modules)
|
||||
return;
|
||||
|
@ -272,10 +273,14 @@ static void PrintModulesMachineReadable(const CodeModules *modules) {
|
|||
++module_sequence) {
|
||||
const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
|
||||
u_int64_t base_address = module->base_address();
|
||||
printf("Module%c%s%c%s%c0x%08llx%c0x%08llx%c%d\n",
|
||||
printf("Module%c%s%c%s%c%s%c%s%c0x%08llx%c0x%08llx%c%d\n",
|
||||
kOutputSeparator,
|
||||
StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
|
||||
kOutputSeparator, StripSeparator(module->version()).c_str(),
|
||||
kOutputSeparator,
|
||||
StripSeparator(PathnameStripper::File(module->debug_file())).c_str(),
|
||||
kOutputSeparator,
|
||||
StripSeparator(module->debug_identifier()).c_str(),
|
||||
kOutputSeparator, base_address,
|
||||
kOutputSeparator, base_address + module->size() - 1,
|
||||
kOutputSeparator, base_address == main_address ? 1 : 0);
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
// Copyright (C) 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// postfix_evaluator-inl.h: Postfix (reverse Polish) notation expression
|
||||
// evaluator.
|
||||
//
|
||||
// Documentation in postfix_evaluator.h.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_POSTFIX_EVALUATOR_INL_H__
|
||||
#define PROCESSOR_POSTFIX_EVALUATOR_INL_H__
|
||||
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "processor/postfix_evaluator.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::istringstream;
|
||||
using std::ostringstream;
|
||||
|
||||
|
||||
// A small class used in Evaluate to make sure to clean up the stack
|
||||
// before returning failure.
|
||||
class AutoStackClearer {
|
||||
public:
|
||||
explicit AutoStackClearer(vector<string> *stack) : stack_(stack) {}
|
||||
~AutoStackClearer() { stack_->clear(); }
|
||||
|
||||
private:
|
||||
vector<string> *stack_;
|
||||
};
|
||||
|
||||
|
||||
template<typename ValueType>
|
||||
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
|
||||
DictionaryValidityType *assigned) {
|
||||
// Ensure that the stack is cleared before returning.
|
||||
AutoStackClearer clearer(&stack_);
|
||||
|
||||
// Tokenize, splitting on whitespace.
|
||||
istringstream stream(expression);
|
||||
string token;
|
||||
while (stream >> token) {
|
||||
// There are enough binary operations that do exactly the same thing
|
||||
// (other than the specific operation, of course) that it makes sense
|
||||
// to share as much code as possible.
|
||||
enum BinaryOperation {
|
||||
BINARY_OP_NONE = 0,
|
||||
BINARY_OP_ADD,
|
||||
BINARY_OP_SUBTRACT,
|
||||
BINARY_OP_MULTIPLY,
|
||||
BINARY_OP_DIVIDE_QUOTIENT,
|
||||
BINARY_OP_DIVIDE_MODULUS
|
||||
};
|
||||
|
||||
BinaryOperation operation = BINARY_OP_NONE;
|
||||
if (token == "+")
|
||||
operation = BINARY_OP_ADD;
|
||||
else if (token == "-")
|
||||
operation = BINARY_OP_SUBTRACT;
|
||||
else if (token == "*")
|
||||
operation = BINARY_OP_MULTIPLY;
|
||||
else if (token == "/")
|
||||
operation = BINARY_OP_DIVIDE_QUOTIENT;
|
||||
else if (token == "%")
|
||||
operation = BINARY_OP_DIVIDE_MODULUS;
|
||||
|
||||
if (operation != BINARY_OP_NONE) {
|
||||
// Get the operands.
|
||||
ValueType operand1, operand2;
|
||||
if (!PopValues(&operand1, &operand2))
|
||||
return false;
|
||||
|
||||
// Perform the operation.
|
||||
ValueType result;
|
||||
switch (operation) {
|
||||
case BINARY_OP_ADD:
|
||||
result = operand1 + operand2;
|
||||
break;
|
||||
case BINARY_OP_SUBTRACT:
|
||||
result = operand1 - operand2;
|
||||
break;
|
||||
case BINARY_OP_MULTIPLY:
|
||||
result = operand1 * operand2;
|
||||
break;
|
||||
case BINARY_OP_DIVIDE_QUOTIENT:
|
||||
result = operand1 / operand2;
|
||||
break;
|
||||
case BINARY_OP_DIVIDE_MODULUS:
|
||||
result = operand1 % operand2;
|
||||
break;
|
||||
case BINARY_OP_NONE:
|
||||
// This will not happen, but compilers will want a default or
|
||||
// BINARY_OP_NONE case.
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Save the result.
|
||||
PushValue(result);
|
||||
} else if (token == "^") {
|
||||
// ^ for unary dereference. Can't dereference without memory.
|
||||
if (!memory_)
|
||||
return false;
|
||||
|
||||
ValueType address;
|
||||
if (!PopValue(&address))
|
||||
return false;
|
||||
|
||||
ValueType value;
|
||||
if (!memory_->GetMemoryAtAddress(address, &value))
|
||||
return false;
|
||||
|
||||
PushValue(value);
|
||||
} else if (token == "=") {
|
||||
// = for assignment.
|
||||
ValueType value;
|
||||
if (!PopValue(&value))
|
||||
return false;
|
||||
|
||||
// Assignment is only meaningful when assigning into an identifier.
|
||||
// The identifier must name a variable, not a constant. Variables
|
||||
// begin with '$'.
|
||||
string identifier;
|
||||
if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER)
|
||||
return false;
|
||||
if (identifier.empty() || identifier[0] != '$')
|
||||
return false;
|
||||
|
||||
(*dictionary_)[identifier] = value;
|
||||
if (assigned)
|
||||
(*assigned)[identifier] = true;
|
||||
} else {
|
||||
// The token is not an operator, it's a literal value or an identifier.
|
||||
// Push it onto the stack as-is. Use push_back instead of PushValue
|
||||
// because PushValue pushes ValueType as a string, but token is already
|
||||
// a string.
|
||||
stack_.push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's anything left on the stack, it indicates incomplete execution.
|
||||
// This is a failure case. If the stack is empty, evalution was complete
|
||||
// and successful.
|
||||
return stack_.empty();
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueType>
|
||||
typename PostfixEvaluator<ValueType>::PopResult
|
||||
PostfixEvaluator<ValueType>::PopValueOrIdentifier(
|
||||
ValueType *value, string *identifier) {
|
||||
// There needs to be at least one element on the stack to pop.
|
||||
if (!stack_.size())
|
||||
return POP_RESULT_FAIL;
|
||||
|
||||
string token = stack_.back();
|
||||
stack_.pop_back();
|
||||
|
||||
// First, try to treat the value as a literal. In order for this to
|
||||
// succed, the entire string must be parseable as ValueType. If this
|
||||
// isn't possible, it can't be a literal, so treat it as an identifier
|
||||
// instead.
|
||||
istringstream token_stream(token);
|
||||
ValueType literal;
|
||||
if (token_stream >> literal && token_stream.peek() == EOF) {
|
||||
if (value) {
|
||||
*value = literal;
|
||||
}
|
||||
return POP_RESULT_VALUE;
|
||||
} else {
|
||||
if (identifier) {
|
||||
*identifier = token;
|
||||
}
|
||||
return POP_RESULT_IDENTIFIER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueType>
|
||||
bool PostfixEvaluator<ValueType>::PopValue(ValueType *value) {
|
||||
ValueType literal;
|
||||
string token;
|
||||
PopResult result;
|
||||
if ((result = PopValueOrIdentifier(&literal, &token)) == POP_RESULT_FAIL) {
|
||||
return false;
|
||||
} else if (result == POP_RESULT_VALUE) {
|
||||
// This is the easy case.
|
||||
*value = literal;
|
||||
} else { // result == POP_RESULT_IDENTIFIER
|
||||
// There was an identifier at the top of the stack. Resolve it to a
|
||||
// value by looking it up in the dictionary.
|
||||
typename DictionaryType::const_iterator iterator =
|
||||
dictionary_->find(token);
|
||||
if (iterator == dictionary_->end()) {
|
||||
// The identifier wasn't found in the dictionary. Don't imply any
|
||||
// default value, just fail.
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = iterator->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueType>
|
||||
bool PostfixEvaluator<ValueType>::PopValues(ValueType *value1,
|
||||
ValueType *value2) {
|
||||
return PopValue(value2) && PopValue(value1);
|
||||
}
|
||||
|
||||
|
||||
template<typename ValueType>
|
||||
void PostfixEvaluator<ValueType>::PushValue(const ValueType &value) {
|
||||
ostringstream token_stream;
|
||||
token_stream << value;
|
||||
stack_.push_back(token_stream.str());
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_POSTFIX_EVALUATOR_INL_H__
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
|
||||
#include "processor/range_map.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
|
||||
namespace google_breakpad {
|
||||
|
@ -50,8 +51,15 @@ bool RangeMap<AddressType, EntryType>::StoreRange(const AddressType &base,
|
|||
AddressType high = base + size - 1;
|
||||
|
||||
// Check for undersize or overflow.
|
||||
if (size <= 0 || high < base)
|
||||
if (size <= 0 || high < base) {
|
||||
// The processor will hit this case too frequently with common symbol
|
||||
// files in the size == 0 case, which is more suited to a DEBUG channel.
|
||||
// Filter those out since there's no DEBUG channel at the moment.
|
||||
BPLOG_IF(INFO, size != 0) << "StoreRange failed, " << HexString(base) <<
|
||||
"+" << HexString(size) << ", " <<
|
||||
HexString(high);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that this range does not overlap with another one already in the
|
||||
// map.
|
||||
|
@ -62,14 +70,27 @@ bool RangeMap<AddressType, EntryType>::StoreRange(const AddressType &base,
|
|||
// Some other range begins in the space used by this range. It may be
|
||||
// contained within the space used by this range, or it may extend lower.
|
||||
// Regardless, it is an error.
|
||||
AddressType other_base = iterator_base->second.base();
|
||||
AddressType other_size = iterator_base->first - other_base + 1;
|
||||
BPLOG(INFO) << "StoreRange failed, an existing range is contained by or "
|
||||
"extends lower than the new range: new " <<
|
||||
HexString(base) << "+" << HexString(size) << ", existing " <<
|
||||
HexString(other_base) << "+" << HexString(other_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iterator_high != map_.end()) {
|
||||
if (iterator_high->second.base() <= high) {
|
||||
// The range above this one overlaps with this one. It may fully
|
||||
// contain this range, or it may begin within this range and extend
|
||||
// contain this range, or it may begin within this range and extend
|
||||
// higher. Regardless, it's an error.
|
||||
AddressType other_base = iterator_high->second.base();
|
||||
AddressType other_size = iterator_high->first - other_base + 1;
|
||||
BPLOG(INFO) << "StoreRange failed, an existing range contains or "
|
||||
"extends higher than the new range: new " <<
|
||||
HexString(base) << "+" << HexString(size) <<
|
||||
", existing " <<
|
||||
HexString(other_base) << "+" << HexString(other_size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -85,8 +106,8 @@ template<typename AddressType, typename EntryType>
|
|||
bool RangeMap<AddressType, EntryType>::RetrieveRange(
|
||||
const AddressType &address, EntryType *entry,
|
||||
AddressType *entry_base, AddressType *entry_size) const {
|
||||
if (!entry)
|
||||
return false;
|
||||
BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveRange requires |entry|";
|
||||
assert(entry);
|
||||
|
||||
MapConstIterator iterator = map_.lower_bound(address);
|
||||
if (iterator == map_.end())
|
||||
|
@ -114,8 +135,8 @@ template<typename AddressType, typename EntryType>
|
|||
bool RangeMap<AddressType, EntryType>::RetrieveNearestRange(
|
||||
const AddressType &address, EntryType *entry,
|
||||
AddressType *entry_base, AddressType *entry_size) const {
|
||||
if (!entry)
|
||||
return false;
|
||||
BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveNearestRange requires |entry|";
|
||||
assert(entry);
|
||||
|
||||
// If address is within a range, RetrieveRange can handle it.
|
||||
if (RetrieveRange(address, entry, entry_base, entry_size))
|
||||
|
@ -145,8 +166,13 @@ template<typename AddressType, typename EntryType>
|
|||
bool RangeMap<AddressType, EntryType>::RetrieveRangeAtIndex(
|
||||
int index, EntryType *entry,
|
||||
AddressType *entry_base, AddressType *entry_size) const {
|
||||
if (!entry || index >= GetCount())
|
||||
BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveRangeAtIndex requires |entry|";
|
||||
assert(entry);
|
||||
|
||||
if (index >= GetCount()) {
|
||||
BPLOG(ERROR) << "Index out of range: " << index << "/" << GetCount();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk through the map. Although it's ordered, it's not a vector, so it
|
||||
// can't be addressed directly by index.
|
||||
|
|
|
@ -1,127 +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.
|
||||
|
||||
// range_map.h: Range maps.
|
||||
//
|
||||
// A range map associates a range of addresses with a specific object. This
|
||||
// is useful when certain objects of variable size are located within an
|
||||
// address space. The range map makes it simple to determine which object is
|
||||
// associated with a specific address, which may be any address within the
|
||||
// range associated with an object.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_RANGE_MAP_H__
|
||||
#define PROCESSOR_RANGE_MAP_H__
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
class RangeMap {
|
||||
public:
|
||||
RangeMap() : map_() {}
|
||||
|
||||
// Inserts a range into the map. Returns false for a parameter error,
|
||||
// or if the location of the range would conflict with a range already
|
||||
// stored in the map.
|
||||
bool StoreRange(const AddressType &base,
|
||||
const AddressType &size,
|
||||
const EntryType &entry);
|
||||
|
||||
// Locates the range encompassing the supplied address. If there is
|
||||
// no such range, or if there is a parameter error, returns false.
|
||||
// entry_base and entry_size, if non-NULL, are set to the base and size
|
||||
// of the entry's range.
|
||||
bool RetrieveRange(const AddressType &address, EntryType *entry,
|
||||
AddressType *entry_base, AddressType *entry_size) const;
|
||||
|
||||
// Locates the range encompassing the supplied address, if one exists.
|
||||
// If no range encompasses the supplied address, locates the nearest range
|
||||
// to the supplied address that is lower than the address. Returns false
|
||||
// if no range meets these criteria. entry_base and entry_size, if
|
||||
// non-NULL, are set to the base and size of the entry's range.
|
||||
bool RetrieveNearestRange(const AddressType &address, EntryType *entry,
|
||||
AddressType *entry_base, AddressType *entry_size)
|
||||
const;
|
||||
|
||||
// Treating all ranges as a list ordered by the address spaces that they
|
||||
// occupy, locates the range at the index specified by index. Returns
|
||||
// false if index is larger than the number of ranges stored, or if another
|
||||
// parameter error occurs. entry_base and entry_size, if non-NULL, are set
|
||||
// to the base and size of the entry's range.
|
||||
//
|
||||
// RetrieveRangeAtIndex is not optimized for speedy operation.
|
||||
bool RetrieveRangeAtIndex(int index, EntryType *entry,
|
||||
AddressType *entry_base, AddressType *entry_size)
|
||||
const;
|
||||
|
||||
// Returns the number of ranges stored in the RangeMap.
|
||||
int GetCount() const;
|
||||
|
||||
// Empties the range map, restoring it to the state it was when it was
|
||||
// initially created.
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
class Range {
|
||||
public:
|
||||
Range(const AddressType &base, const EntryType &entry)
|
||||
: base_(base), entry_(entry) {}
|
||||
|
||||
AddressType base() const { return base_; }
|
||||
EntryType entry() const { return entry_; }
|
||||
|
||||
private:
|
||||
// The base address of the range. The high address does not need to
|
||||
// be stored, because RangeMap uses it as the key to the map.
|
||||
const AddressType base_;
|
||||
|
||||
// The entry corresponding to a range.
|
||||
const EntryType entry_;
|
||||
};
|
||||
|
||||
// Convenience types.
|
||||
typedef std::map<AddressType, Range> AddressToRangeMap;
|
||||
typedef typename AddressToRangeMap::const_iterator MapConstIterator;
|
||||
typedef typename AddressToRangeMap::value_type MapValue;
|
||||
|
||||
// Maps the high address of each range to a EntryType.
|
||||
AddressToRangeMap map_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_RANGE_MAP_H__
|
|
@ -1,113 +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.
|
||||
|
||||
// simple_symbol_supplier.cc: A simple SymbolSupplier implementation
|
||||
//
|
||||
// See simple_symbol_supplier.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/system_info.h"
|
||||
#include "processor/pathname_stripper.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static bool file_exists(const string &file_name) {
|
||||
struct stat sb;
|
||||
return stat(file_name.c_str(), &sb) == 0;
|
||||
}
|
||||
|
||||
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile(
|
||||
const CodeModule *module, const SystemInfo *system_info,
|
||||
string *symbol_file) {
|
||||
assert(symbol_file);
|
||||
for (unsigned int path_index = 0; path_index < paths_.size(); ++path_index) {
|
||||
SymbolResult result;
|
||||
if ((result = GetSymbolFileAtPath(module, system_info, paths_[path_index],
|
||||
symbol_file)) != NOT_FOUND) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath(
|
||||
const CodeModule *module, const SystemInfo *system_info,
|
||||
const string &root_path, string *symbol_file) {
|
||||
assert(symbol_file);
|
||||
if (!module)
|
||||
return NOT_FOUND;
|
||||
|
||||
// Start with the base path.
|
||||
string path = root_path;
|
||||
|
||||
// Append the debug (pdb) file name as a directory name.
|
||||
path.append("/");
|
||||
string debug_file_name = PathnameStripper::File(module->debug_file());
|
||||
if (debug_file_name.empty())
|
||||
return NOT_FOUND;
|
||||
path.append(debug_file_name);
|
||||
|
||||
// Append the identifier as a directory name.
|
||||
path.append("/");
|
||||
string identifier = module->debug_identifier();
|
||||
if (identifier.empty())
|
||||
return NOT_FOUND;
|
||||
path.append(identifier);
|
||||
|
||||
// Transform the debug file name into one ending in .sym. If the existing
|
||||
// name ends in .pdb, strip the .pdb. Otherwise, add .sym to the non-.pdb
|
||||
// name.
|
||||
path.append("/");
|
||||
string debug_file_extension =
|
||||
debug_file_name.substr(debug_file_name.size() - 4);
|
||||
transform(debug_file_extension.begin(), debug_file_extension.end(),
|
||||
debug_file_extension.begin(), tolower);
|
||||
if (debug_file_extension == ".pdb") {
|
||||
path.append(debug_file_name.substr(0, debug_file_name.size() - 4));
|
||||
} else {
|
||||
path.append(debug_file_name);
|
||||
}
|
||||
path.append(".sym");
|
||||
|
||||
if (!file_exists(path))
|
||||
return NOT_FOUND;
|
||||
|
||||
*symbol_file = path;
|
||||
return FOUND;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,168 +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.
|
||||
|
||||
// stackwalker.cc: Generic stackwalker.
|
||||
//
|
||||
// See stackwalker.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/code_modules.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "google_breakpad/processor/symbol_supplier.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
Stackwalker::Stackwalker(const SystemInfo *system_info,
|
||||
MemoryRegion *memory,
|
||||
const CodeModules *modules,
|
||||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver)
|
||||
: system_info_(system_info),
|
||||
memory_(memory),
|
||||
modules_(modules),
|
||||
supplier_(supplier),
|
||||
resolver_(resolver) {
|
||||
}
|
||||
|
||||
|
||||
bool Stackwalker::Walk(CallStack *stack) {
|
||||
assert(stack);
|
||||
stack->Clear();
|
||||
|
||||
// stack_frame_info parallels the CallStack. The vector is passed to the
|
||||
// GetCallerFrame function. It contains information that may be helpful
|
||||
// for stackwalking.
|
||||
vector< linked_ptr<StackFrameInfo> > stack_frame_info;
|
||||
|
||||
// Begin with the context frame, and keep getting callers until there are
|
||||
// no more.
|
||||
|
||||
// Take ownership of the pointer returned by GetContextFrame.
|
||||
scoped_ptr<StackFrame> frame(GetContextFrame());
|
||||
|
||||
while (frame.get()) {
|
||||
// frame already contains a good frame with properly set instruction and
|
||||
// frame_pointer fields. The frame structure comes from either the
|
||||
// context frame (above) or a caller frame (below).
|
||||
|
||||
linked_ptr<StackFrameInfo> frame_info;
|
||||
|
||||
// Resolve the module information, if a module map was provided.
|
||||
if (modules_) {
|
||||
const CodeModule *module =
|
||||
modules_->GetModuleForAddress(frame->instruction);
|
||||
if (module) {
|
||||
frame->module = module;
|
||||
if (resolver_ &&
|
||||
!resolver_->HasModule(frame->module->code_file()) &&
|
||||
supplier_) {
|
||||
string symbol_file;
|
||||
SymbolSupplier::SymbolResult symbol_result =
|
||||
supplier_->GetSymbolFile(module, system_info_, &symbol_file);
|
||||
|
||||
switch (symbol_result) {
|
||||
case SymbolSupplier::FOUND:
|
||||
resolver_->LoadModule(frame->module->code_file(), symbol_file);
|
||||
break;
|
||||
case SymbolSupplier::NOT_FOUND:
|
||||
break; // nothing to do
|
||||
case SymbolSupplier::INTERRUPT:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
frame_info.reset(resolver_->FillSourceLineInfo(frame.get()));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the frame to the call stack. Relinquish the ownership claim
|
||||
// over the frame, because the stack now owns it.
|
||||
stack->frames_.push_back(frame.release());
|
||||
|
||||
// Add the frame info to the parallel stack.
|
||||
stack_frame_info.push_back(frame_info);
|
||||
frame_info.reset(NULL);
|
||||
|
||||
// Get the next frame and take ownership.
|
||||
frame.reset(GetCallerFrame(stack, stack_frame_info));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
Stackwalker* Stackwalker::StackwalkerForCPU(
|
||||
const SystemInfo *system_info,
|
||||
MinidumpContext *context,
|
||||
MemoryRegion *memory,
|
||||
const CodeModules *modules,
|
||||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver) {
|
||||
if (!context)
|
||||
return NULL;
|
||||
|
||||
Stackwalker *cpu_stackwalker = NULL;
|
||||
|
||||
u_int32_t cpu = context->GetContextCPU();
|
||||
switch (cpu) {
|
||||
case MD_CONTEXT_X86:
|
||||
cpu_stackwalker = new StackwalkerX86(system_info,
|
||||
context->GetContextX86(),
|
||||
memory, modules, supplier,
|
||||
resolver);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_PPC:
|
||||
cpu_stackwalker = new StackwalkerPPC(system_info,
|
||||
context->GetContextPPC(),
|
||||
memory, modules, supplier,
|
||||
resolver);
|
||||
break;
|
||||
}
|
||||
|
||||
return cpu_stackwalker;
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,138 +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.
|
||||
|
||||
// stackwalker_ppc.cc: ppc-specific stackwalker.
|
||||
//
|
||||
// See stackwalker_ppc.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
StackwalkerPPC::StackwalkerPPC(const SystemInfo *system_info,
|
||||
const MDRawContextPPC *context,
|
||||
MemoryRegion *memory,
|
||||
const CodeModules *modules,
|
||||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver)
|
||||
: Stackwalker(system_info, memory, modules, supplier, resolver),
|
||||
context_(context) {
|
||||
if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
|
||||
// This implementation only covers 32-bit ppc CPUs. The limits of the
|
||||
// supplied stack are invalid. Mark memory_ = NULL, which will cause
|
||||
// stackwalking to fail.
|
||||
memory_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerPPC::GetContextFrame() {
|
||||
if (!context_ || !memory_)
|
||||
return NULL;
|
||||
|
||||
StackFramePPC *frame = new StackFramePPC();
|
||||
|
||||
// The instruction pointer is stored directly in a register, so pull it
|
||||
// straight out of the CPU context structure.
|
||||
frame->context = *context_;
|
||||
frame->context_validity = StackFramePPC::CONTEXT_VALID_ALL;
|
||||
frame->instruction = frame->context.srr0;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerPPC::GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
|
||||
if (!memory_ || !stack)
|
||||
return NULL;
|
||||
|
||||
// The instruction pointers for previous frames are saved on the stack.
|
||||
// The typical ppc calling convention is for the called procedure to store
|
||||
// its return address in the calling procedure's stack frame at 8(%r1),
|
||||
// and to allocate its own stack frame by decrementing %r1 (the stack
|
||||
// pointer) and saving the old value of %r1 at 0(%r1). Because the ppc has
|
||||
// no hardware stack, there is no distinction between the stack pointer and
|
||||
// frame pointer, and what is typically thought of as the frame pointer on
|
||||
// an x86 is usually referred to as the stack pointer on a ppc.
|
||||
|
||||
StackFramePPC *last_frame = static_cast<StackFramePPC*>(
|
||||
stack->frames()->back());
|
||||
|
||||
// A caller frame must reside higher in memory than its callee frames.
|
||||
// Anything else is an error, or an indication that we've reached the
|
||||
// end of the stack.
|
||||
u_int32_t stack_pointer;
|
||||
if (!memory_->GetMemoryAtAddress(last_frame->context.gpr[1],
|
||||
&stack_pointer) ||
|
||||
stack_pointer <= last_frame->context.gpr[1]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Mac OS X/Darwin gives 1 as the return address from the bottom-most
|
||||
// frame in a stack (a thread's entry point). I haven't found any
|
||||
// documentation on this, but 0 or 1 would be bogus return addresses,
|
||||
// so check for them here and return false (end of stack) when they're
|
||||
// hit to avoid having a phantom frame.
|
||||
u_int32_t instruction;
|
||||
if (!memory_->GetMemoryAtAddress(stack_pointer + 8, &instruction) ||
|
||||
instruction <= 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFramePPC *frame = new StackFramePPC();
|
||||
|
||||
frame->context = last_frame->context;
|
||||
frame->context.srr0 = instruction;
|
||||
frame->context.gpr[1] = stack_pointer;
|
||||
frame->context_validity = StackFramePPC::CONTEXT_VALID_SRR0 |
|
||||
StackFramePPC::CONTEXT_VALID_GPR1;
|
||||
|
||||
// frame->context.srr0 is the return address, which is one instruction
|
||||
// past the branch that caused us to arrive at the callee. Set
|
||||
// frame_ppc->instruction to four less than that. Since all ppc
|
||||
// instructions are 4 bytes wide, this is the address of the branch
|
||||
// instruction. This allows source line information to match up with the
|
||||
// line that contains a function call. Callers that require the exact
|
||||
// return address value may access the context.srr0 field of StackFramePPC.
|
||||
frame->instruction = frame->context.srr0 - 4;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,428 +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.
|
||||
|
||||
// stackwalker_x86.cc: x86-specific stackwalker.
|
||||
//
|
||||
// See stackwalker_x86.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
#include "processor/stackwalker_x86.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/code_modules.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
StackwalkerX86::StackwalkerX86(const SystemInfo *system_info,
|
||||
const MDRawContextX86 *context,
|
||||
MemoryRegion *memory,
|
||||
const CodeModules *modules,
|
||||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver)
|
||||
: Stackwalker(system_info, memory, modules, supplier, resolver),
|
||||
context_(context) {
|
||||
if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
|
||||
// The x86 is a 32-bit CPU, the limits of the supplied stack are invalid.
|
||||
// Mark memory_ = NULL, which will cause stackwalking to fail.
|
||||
memory_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerX86::GetContextFrame() {
|
||||
if (!context_ || !memory_)
|
||||
return NULL;
|
||||
|
||||
StackFrameX86 *frame = new StackFrameX86();
|
||||
|
||||
// The instruction pointer is stored directly in a register, so pull it
|
||||
// straight out of the CPU context structure.
|
||||
frame->context = *context_;
|
||||
frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL;
|
||||
frame->instruction = frame->context.eip;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerX86::GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
|
||||
if (!memory_ || !stack)
|
||||
return NULL;
|
||||
|
||||
StackFrameX86 *last_frame = static_cast<StackFrameX86*>(
|
||||
stack->frames()->back());
|
||||
StackFrameInfo *last_frame_info = stack_frame_info.back().get();
|
||||
|
||||
// This stackwalker sets each frame's %esp to its value immediately prior
|
||||
// to the CALL into the callee. This means that %esp points to the last
|
||||
// callee argument pushed onto the stack, which may not be where %esp points
|
||||
// after the callee returns. Specifically, the value is correct for the
|
||||
// cdecl calling convention, but not other conventions. The cdecl
|
||||
// convention requires a caller to pop its callee's arguments from the
|
||||
// stack after the callee returns. This is usually accomplished by adding
|
||||
// the known size of the arguments to %esp. Other calling conventions,
|
||||
// including stdcall, thiscall, and fastcall, require the callee to pop any
|
||||
// parameters stored on the stack before returning. This is usually
|
||||
// accomplished by using the RET n instruction, which pops n bytes off
|
||||
// the stack after popping the return address.
|
||||
//
|
||||
// Because each frame's %esp will point to a location on the stack after
|
||||
// callee arguments have been PUSHed, when locating things in a stack frame
|
||||
// relative to %esp, the size of the arguments to the callee need to be
|
||||
// taken into account. This seems a little bit unclean, but it's better
|
||||
// than the alternative, which would need to take these same things into
|
||||
// account, but only for cdecl functions. With this implementation, we get
|
||||
// to be agnostic about each function's calling convention. Furthermore,
|
||||
// this is how Windows debugging tools work, so it means that the %esp
|
||||
// values produced by this stackwalker directly correspond to the %esp
|
||||
// values you'll see there.
|
||||
//
|
||||
// If the last frame has no callee (because it's the context frame), just
|
||||
// set the callee parameter size to 0: the stack pointer can't point to
|
||||
// callee arguments because there's no callee. This is correct as long
|
||||
// as the context wasn't captured while arguments were being pushed for
|
||||
// a function call. Note that there may be functions whose parameter sizes
|
||||
// are unknown, 0 is also used in that case. When that happens, it should
|
||||
// be possible to walk to the next frame without reference to %esp.
|
||||
|
||||
int frames_already_walked = stack_frame_info.size();
|
||||
u_int32_t last_frame_callee_parameter_size = 0;
|
||||
if (frames_already_walked >= 2) {
|
||||
StackFrameInfo *last_frame_callee_info =
|
||||
stack_frame_info[frames_already_walked - 2].get();
|
||||
if (last_frame_callee_info &&
|
||||
last_frame_callee_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) {
|
||||
last_frame_callee_parameter_size =
|
||||
last_frame_callee_info->parameter_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used
|
||||
// in each program string, and their previous values are known, so set them
|
||||
// here. .cbCalleeParams is a Breakpad extension that allows us to use
|
||||
// the PostfixEvaluator engine when certain types of debugging information
|
||||
// are present without having to write the constants into the program string
|
||||
// as literals.
|
||||
PostfixEvaluator<u_int32_t>::DictionaryType dictionary;
|
||||
dictionary["$ebp"] = last_frame->context.ebp;
|
||||
dictionary["$esp"] = last_frame->context.esp;
|
||||
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
|
||||
|
||||
if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) {
|
||||
// FPO debugging data is available. Initialize constants.
|
||||
dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
|
||||
dictionary[".cbLocals"] = last_frame_info->local_size;
|
||||
dictionary[".raSearchStart"] = last_frame->context.esp +
|
||||
last_frame_callee_parameter_size +
|
||||
last_frame_info->local_size +
|
||||
last_frame_info->saved_register_size;
|
||||
}
|
||||
if (last_frame_info &&
|
||||
last_frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) {
|
||||
// This is treated separately because it can either come from FPO data or
|
||||
// from other debugging data.
|
||||
dictionary[".cbParams"] = last_frame_info->parameter_size;
|
||||
}
|
||||
|
||||
// Decide what type of program string to use. The program string is in
|
||||
// postfix notation and will be passed to PostfixEvaluator::Evaluate.
|
||||
// Given the dictionary and the program string, it is possible to compute
|
||||
// the return address and the values of other registers in the calling
|
||||
// function. When encountering a nontraditional frame (one which takes
|
||||
// advantage of FPO), the stack may need to be scanned for these values.
|
||||
// For traditional frames, simple deterministic dereferencing suffices
|
||||
// without any need for scanning. The results of program string evaluation
|
||||
// will be used to determine whether to scan for better values.
|
||||
string program_string;
|
||||
bool traditional_frame = true;
|
||||
bool recover_ebp = true;
|
||||
if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) {
|
||||
// FPO data available.
|
||||
traditional_frame = false;
|
||||
if (!last_frame_info->program_string.empty()) {
|
||||
// The FPO data has its own program string, which will tell us how to
|
||||
// get to the caller frame, and may even fill in the values of
|
||||
// nonvolatile registers and provide pointers to local variables and
|
||||
// parameters. In some cases, particularly with program strings that use
|
||||
// .raSearchStart, the stack may need to be scanned afterward.
|
||||
program_string = last_frame_info->program_string;
|
||||
} else if (last_frame_info->allocates_base_pointer) {
|
||||
// The function corresponding to the last frame doesn't use the frame
|
||||
// pointer for conventional purposes, but it does allocate a new
|
||||
// frame pointer and use it for its own purposes. Its callee's
|
||||
// information is still accessed relative to %esp, and the previous
|
||||
// value of %ebp can be recovered from a location in its stack frame,
|
||||
// within the saved-register area.
|
||||
//
|
||||
// Functions that fall into this category use the %ebp register for
|
||||
// a purpose other than the frame pointer. They restore the caller's
|
||||
// %ebp before returning. These functions create their stack frame
|
||||
// after a CALL by decrementing the stack pointer in an amount
|
||||
// sufficient to store local variables, and then PUSHing saved
|
||||
// registers onto the stack. Arguments to a callee function, if any,
|
||||
// are PUSHed after that. Walking up to the caller, therefore,
|
||||
// can be done solely with calculations relative to the stack pointer
|
||||
// (%esp). The return address is recovered from the memory location
|
||||
// above the known sizes of the callee's parameters, saved registers,
|
||||
// and locals. The caller's stack pointer (the value of %esp when
|
||||
// the caller executed CALL) is the location immediately above the
|
||||
// saved return address. The saved value of %ebp to be restored for
|
||||
// the caller is at a known location in the saved-register area of
|
||||
// the stack frame.
|
||||
//
|
||||
// For this type of frame, MSVC 14 (from Visual Studio 8/2005) in
|
||||
// link-time code generation mode (/LTCG and /GL) can generate erroneous
|
||||
// debugging data. The reported size of saved registers can be 0,
|
||||
// which is clearly an error because these frames must, at the very
|
||||
// least, save %ebp. For this reason, in addition to those given above
|
||||
// about the use of .raSearchStart, the stack may need to be scanned
|
||||
// for a better return address and a better frame pointer after the
|
||||
// program string is evaluated.
|
||||
//
|
||||
// %eip_new = *(%esp_old + callee_params + saved_regs + locals)
|
||||
// %ebp_new = *(%esp_old + callee_params + saved_regs - 8)
|
||||
// %esp_new = %esp_old + callee_params + saved_regs + locals + 4
|
||||
program_string = "$eip .raSearchStart ^ = "
|
||||
"$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = "
|
||||
"$esp .raSearchStart 4 + =";
|
||||
} else {
|
||||
// The function corresponding to the last frame doesn't use %ebp at
|
||||
// all. The callee frame is located relative to %esp. %ebp is reset
|
||||
// to itself only to cause it to appear to have been set in
|
||||
// dictionary_validity.
|
||||
//
|
||||
// The called procedure's instruction pointer and stack pointer are
|
||||
// recovered in the same way as the case above, except that no
|
||||
// frame pointer (%ebp) is used at all, so it is not saved anywhere
|
||||
// in the callee's stack frame and does not need to be recovered.
|
||||
// Because %ebp wasn't used in the callee, whatever value it has
|
||||
// is the value that it had in the caller, so it can be carried
|
||||
// straight through without bringing its validity into question.
|
||||
//
|
||||
// Because of the use of .raSearchStart, the stack will possibly be
|
||||
// examined to locate a better return address after program string
|
||||
// evaluation. The stack will not be examined to locate a saved
|
||||
// %ebp value, because these frames do not save (or use) %ebp.
|
||||
//
|
||||
// %eip_new = *(%esp_old + callee_params + saved_regs + locals)
|
||||
// %esp_new = %esp_old + callee_params + saved_regs + locals + 4
|
||||
// %ebp_new = %ebp_old
|
||||
program_string = "$eip .raSearchStart ^ = "
|
||||
"$esp .raSearchStart 4 + = "
|
||||
"$ebp $ebp =";
|
||||
recover_ebp = false;
|
||||
}
|
||||
} else {
|
||||
// No FPO information is available for the last frame. Assume that the
|
||||
// standard %ebp-using x86 calling convention is in use.
|
||||
//
|
||||
// The typical x86 calling convention, when frame pointers are present,
|
||||
// is for the calling procedure to use CALL, which pushes the return
|
||||
// address onto the stack and sets the instruction pointer (%eip) to
|
||||
// the entry point of the called routine. The called routine then
|
||||
// PUSHes the calling routine's frame pointer (%ebp) onto the stack
|
||||
// before copying the stack pointer (%esp) to the frame pointer (%ebp).
|
||||
// Therefore, the calling procedure's frame pointer is always available
|
||||
// by dereferencing the called procedure's frame pointer, and the return
|
||||
// address is always available at the memory location immediately above
|
||||
// the address pointed to by the called procedure's frame pointer. The
|
||||
// calling procedure's stack pointer (%esp) is 8 higher than the value
|
||||
// of the called procedure's frame pointer at the time the calling
|
||||
// procedure made the CALL: 4 bytes for the return address pushed by the
|
||||
// CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
|
||||
// pointer.
|
||||
//
|
||||
// Instruction and frame pointer recovery for these traditional frames is
|
||||
// entirely deterministic, and the stack will not be scanned after
|
||||
// recovering these values.
|
||||
//
|
||||
// %eip_new = *(%ebp_old + 4)
|
||||
// %esp_new = %ebp_old + 8
|
||||
// %ebp_new = *(%ebp_old)
|
||||
program_string = "$eip $ebp 4 + ^ = "
|
||||
"$esp $ebp 8 + = "
|
||||
"$ebp $ebp ^ =";
|
||||
}
|
||||
|
||||
// Now crank it out, making sure that the program string set the three
|
||||
// required variables.
|
||||
PostfixEvaluator<u_int32_t> evaluator =
|
||||
PostfixEvaluator<u_int32_t>(&dictionary, memory_);
|
||||
PostfixEvaluator<u_int32_t>::DictionaryValidityType dictionary_validity;
|
||||
if (!evaluator.Evaluate(program_string, &dictionary_validity) ||
|
||||
dictionary_validity.find("$eip") == dictionary_validity.end() ||
|
||||
dictionary_validity.find("$esp") == dictionary_validity.end() ||
|
||||
dictionary_validity.find("$ebp") == dictionary_validity.end()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If this stack frame did not use %ebp in a traditional way, locating the
|
||||
// return address isn't entirely deterministic. In that case, the stack
|
||||
// can be scanned to locate the return address.
|
||||
//
|
||||
// Even in nontraditional frames, if program string evaluation resulted in
|
||||
// both %eip and %ebp values of 0, trust that the end of the stack has been
|
||||
// reached and don't scan for anything else.
|
||||
if (!traditional_frame &&
|
||||
(dictionary["$eip"] != 0 || dictionary["$ebp"] != 0)) {
|
||||
int offset = 0;
|
||||
|
||||
// This scan can only be done if a CodeModules object is available, to
|
||||
// check that candidate return addresses are in fact inside a module.
|
||||
//
|
||||
// TODO(mmentovai): This ignores dynamically-generated code. One possible
|
||||
// solution is to check the minidump's memory map to see if the candidate
|
||||
// %eip value comes from a mapped executable page, although this would
|
||||
// require dumps that contain MINIDUMP_MEMORY_INFO, which the Breakpad
|
||||
// client doesn't currently write (it would need to call MiniDumpWriteDump
|
||||
// with the MiniDumpWithFullMemoryInfo type bit set). Even given this
|
||||
// ability, older OSes (pre-XP SP2) and CPUs (pre-P4) don't enforce
|
||||
// an independent execute privilege on memory pages.
|
||||
|
||||
u_int32_t eip = dictionary["$eip"];
|
||||
if (modules_ && !modules_->GetModuleForAddress(eip)) {
|
||||
const int kRASearchWords = 15;
|
||||
|
||||
// The instruction pointer at .raSearchStart was invalid, so start
|
||||
// looking one 32-bit word above that location.
|
||||
u_int32_t location_start = dictionary[".raSearchStart"] + 4;
|
||||
|
||||
for (u_int32_t location = location_start;
|
||||
location <= location_start + kRASearchWords * 4;
|
||||
location += 4) {
|
||||
if (!memory_->GetMemoryAtAddress(location, &eip))
|
||||
break;
|
||||
|
||||
if (modules_->GetModuleForAddress(eip)) {
|
||||
// This is a better return address that what program string
|
||||
// evaluation found. Use it, and set %esp to the location above the
|
||||
// one where the return address was found.
|
||||
//
|
||||
// TODO(mmentovai): The return-address check can be made even
|
||||
// stronger in modules for which debugging data is available. In
|
||||
// that case, it's possible to check that the candidate return
|
||||
// address is inside a known function.
|
||||
|
||||
dictionary["$eip"] = eip;
|
||||
dictionary["$esp"] = location + 4;
|
||||
offset = location - location_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When trying to recover the previous value of the frame pointer (%ebp),
|
||||
// start looking at the lowest possible address in the saved-register
|
||||
// area, and look at the entire saved register area, increased by the
|
||||
// size of |offset| to account for additional data that may be on the
|
||||
// stack. The scan is performed from the highest possible address to
|
||||
// the lowest, because we expect that the function's prolog would have
|
||||
// saved %ebp early.
|
||||
u_int32_t ebp = dictionary["$ebp"];
|
||||
u_int32_t value; // throwaway variable to check pointer validity
|
||||
if (recover_ebp && !memory_->GetMemoryAtAddress(ebp, &value)) {
|
||||
int fp_search_bytes = last_frame_info->saved_register_size + offset;
|
||||
u_int32_t location_end = last_frame->context.esp +
|
||||
last_frame_callee_parameter_size;
|
||||
|
||||
for (u_int32_t location = location_end + fp_search_bytes;
|
||||
location >= location_end;
|
||||
location -= 4) {
|
||||
if (!memory_->GetMemoryAtAddress(location, &ebp))
|
||||
break;
|
||||
|
||||
if (memory_->GetMemoryAtAddress(ebp, &value)) {
|
||||
// The candidate value is a pointer to the same memory region
|
||||
// (the stack). Prefer it as a recovered %ebp result.
|
||||
dictionary["$ebp"] = ebp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Treat an instruction address of 0 as end-of-stack. Treat incorrect stack
|
||||
// direction as end-of-stack to enforce progress and avoid infinite loops.
|
||||
if (dictionary["$eip"] == 0 ||
|
||||
dictionary["$esp"] <= last_frame->context.esp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameX86 *frame = new StackFrameX86();
|
||||
|
||||
frame->context = last_frame->context;
|
||||
frame->context.eip = dictionary["$eip"];
|
||||
frame->context.esp = dictionary["$esp"];
|
||||
frame->context.ebp = dictionary["$ebp"];
|
||||
frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
|
||||
StackFrameX86::CONTEXT_VALID_ESP |
|
||||
StackFrameX86::CONTEXT_VALID_EBP;
|
||||
|
||||
// These are nonvolatile (callee-save) registers, and the program string
|
||||
// may have filled them in.
|
||||
if (dictionary_validity.find("$ebx") != dictionary_validity.end()) {
|
||||
frame->context.ebx = dictionary["$ebx"];
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX;
|
||||
}
|
||||
if (dictionary_validity.find("$esi") != dictionary_validity.end()) {
|
||||
frame->context.esi = dictionary["$esi"];
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI;
|
||||
}
|
||||
if (dictionary_validity.find("$edi") != dictionary_validity.end()) {
|
||||
frame->context.edi = dictionary["$edi"];
|
||||
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
|
||||
}
|
||||
|
||||
// frame->context.eip is the return address, which is one instruction
|
||||
// past the CALL that caused us to arrive at the callee. Set
|
||||
// frame->instruction to one less than that. This won't reference the
|
||||
// beginning of the CALL instruction, but it's guaranteed to be within the
|
||||
// CALL, which is sufficient to get the source line information to match up
|
||||
// with the line that contains a function call. Callers that require the
|
||||
// exact return address value may access the context.eip field of
|
||||
// StackFrameX86.
|
||||
frame->instruction = frame->context.eip - 1;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -1,19 +1,19 @@
|
|||
OS|Windows NT|5.1.2600 Service Pack 2
|
||||
CPU|x86|GenuineIntel family 6 model 13 stepping 8
|
||||
Crash|EXCEPTION_ACCESS_VIOLATION|0x45|0
|
||||
Module|test_app.exe||0x00400000|0x0042cfff|1
|
||||
Module|dbghelp.dll|5.1.2600.2180|0x59a60000|0x59b00fff|0
|
||||
Module|imm32.dll|5.1.2600.2180|0x76390000|0x763acfff|0
|
||||
Module|psapi.dll|5.1.2600.2180|0x76bf0000|0x76bfafff|0
|
||||
Module|ole32.dll|5.1.2600.2726|0x774e0000|0x7761cfff|0
|
||||
Module|version.dll|5.1.2600.2180|0x77c00000|0x77c07fff|0
|
||||
Module|msvcrt.dll|7.0.2600.2180|0x77c10000|0x77c67fff|0
|
||||
Module|user32.dll|5.1.2600.2622|0x77d40000|0x77dcffff|0
|
||||
Module|advapi32.dll|5.1.2600.2180|0x77dd0000|0x77e6afff|0
|
||||
Module|rpcrt4.dll|5.1.2600.2180|0x77e70000|0x77f00fff|0
|
||||
Module|gdi32.dll|5.1.2600.2818|0x77f10000|0x77f56fff|0
|
||||
Module|kernel32.dll|5.1.2600.2945|0x7c800000|0x7c8f3fff|0
|
||||
Module|ntdll.dll|5.1.2600.2180|0x7c900000|0x7c9affff|0
|
||||
Module|test_app.exe||test_app.pdb|5A9832E5287241C1838ED98914E9B7FF1|0x00400000|0x0042cfff|1
|
||||
Module|dbghelp.dll|5.1.2600.2180|dbghelp.pdb|39559573E21B46F28E286923BE9E6A761|0x59a60000|0x59b00fff|0
|
||||
Module|imm32.dll|5.1.2600.2180|imm32.pdb|2C17A49C251B4C8EB9E2AD13D7D9EA162|0x76390000|0x763acfff|0
|
||||
Module|psapi.dll|5.1.2600.2180|psapi.pdb|A5C3A1F9689F43D8AD228A09293889702|0x76bf0000|0x76bfafff|0
|
||||
Module|ole32.dll|5.1.2600.2726|ole32.pdb|683B65B246F4418796D2EE6D4C55EB112|0x774e0000|0x7761cfff|0
|
||||
Module|version.dll|5.1.2600.2180|version.pdb|180A90C40384463E82DDC45B2C8AB76E2|0x77c00000|0x77c07fff|0
|
||||
Module|msvcrt.dll|7.0.2600.2180|msvcrt.pdb|A678F3C30DED426B839032B996987E381|0x77c10000|0x77c67fff|0
|
||||
Module|user32.dll|5.1.2600.2622|user32.pdb|EE2B714D83A34C9D88027621272F83262|0x77d40000|0x77dcffff|0
|
||||
Module|advapi32.dll|5.1.2600.2180|advapi32.pdb|455D6C5F184D45BBB5C5F30F829751142|0x77dd0000|0x77e6afff|0
|
||||
Module|rpcrt4.dll|5.1.2600.2180|rpcrt4.pdb|BEA45A721DA141DAA3BA86B3A20311532|0x77e70000|0x77f00fff|0
|
||||
Module|gdi32.dll|5.1.2600.2818|gdi32.pdb|C0EA66BE00A64BD7AEF79E443A91869C2|0x77f10000|0x77f56fff|0
|
||||
Module|kernel32.dll|5.1.2600.2945|kernel32.pdb|BCE8785C57B44245A669896B6A19B9542|0x7c800000|0x7c8f3fff|0
|
||||
Module|ntdll.dll|5.1.2600.2180|ntdll.pdb|36515FB5D04345E491F672FA2E2878C02|0x7c900000|0x7c9affff|0
|
||||
|
||||
0|0|test_app.exe|`anonymous namespace'::CrashFunction|c:\test_app.cc|58|0x3
|
||||
0|1|test_app.exe|main|c:\test_app.cc|65|0x4
|
||||
|
|
|
@ -255,11 +255,11 @@ static DWORD WINAPI SendThreadProc(LPVOID param)
|
|||
query_parameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
|
||||
}
|
||||
|
||||
finishedOk = (google_breakpad::CrashReportSender
|
||||
::SendCrashReport(td->send_url,
|
||||
query_parameters,
|
||||
td->dumpFile,
|
||||
td->server_response)
|
||||
google_breakpad::CrashReportSender sender(L"");
|
||||
finishedOk = (sender.SendCrashReport(td->send_url,
|
||||
query_parameters,
|
||||
td->dumpFile,
|
||||
td->server_response)
|
||||
== google_breakpad::RESULT_SUCCEEDED);
|
||||
}
|
||||
PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
|
||||
|
|
Загрузка…
Ссылка в новой задаче