зеркало из https://github.com/mozilla/pjs.git
bug 354980, integrate airbag exception handler library on windows. Compiling by default on windows, but disabled at runtime by default right now. Set the environment var MOZ_AIRBAG=1 to enable it. r=bsmedberg/mento
This commit is contained in:
Родитель
e8e9061ce1
Коммит
72f0b0adcc
|
@ -342,6 +342,10 @@ bin\extensions\inspector@mozilla.org\chrome\icons\default\winInspectorMain.ico
|
|||
; [Additional Browsing Enhancements]
|
||||
[abe]
|
||||
|
||||
; [airbag]
|
||||
bin\crashreporter.exe
|
||||
bin\crashreporter.ini
|
||||
|
||||
[talkback]
|
||||
bin\extensions\talkback@mozilla.org\install.rdf
|
||||
bin\extensions\talkback@mozilla.org\chrome.manifest
|
||||
|
|
|
@ -154,6 +154,7 @@ MOZ_MORKREADER = @MOZ_MORKREADER@
|
|||
MOZ_NO_XPCOM_OBSOLETE = @MOZ_NO_XPCOM_OBSOLETE@
|
||||
MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
|
||||
NS_PRINTING = @NS_PRINTING@
|
||||
MOZ_AIRBAG = @MOZ_AIRBAG@
|
||||
|
||||
MOZ_JAVAXPCOM = @MOZ_JAVAXPCOM@
|
||||
JAVA_INCLUDE_PATH="@JAVA_INCLUDE_PATH@"
|
||||
|
|
|
@ -256,7 +256,7 @@ _OBJS = \
|
|||
$(JRI_STUB_CFILES) \
|
||||
$(addsuffix .$(OBJ_SUFFIX), $(JMC_GEN)) \
|
||||
$(CSRCS:.c=.$(OBJ_SUFFIX)) \
|
||||
$(CPPSRCS:.cpp=.$(OBJ_SUFFIX)) \
|
||||
$(patsubst %.cc,%.$(OBJ_SUFFIX),$(CPPSRCS:.cpp=.$(OBJ_SUFFIX))) \
|
||||
$(CMSRCS:.m=.$(OBJ_SUFFIX)) \
|
||||
$(CMMSRCS:.mm=.$(OBJ_SUFFIX)) \
|
||||
$(ASFILES:.$(ASM_SUFFIX)=.$(OBJ_SUFFIX))
|
||||
|
|
18
configure.in
18
configure.in
|
@ -5491,6 +5491,23 @@ if test -n "${MOZ_JAVAXPCOM}"; then
|
|||
fi
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Airbag crash reporting (on by default on supported platforms)
|
||||
dnl ========================================================
|
||||
|
||||
if test "$OS_ARCH" = "WINNT"; then
|
||||
MOZ_AIRBAG=1
|
||||
fi
|
||||
|
||||
MOZ_ARG_DISABLE_BOOL(airbag,
|
||||
[ --disable-airbag Disable airbag crash reporting],
|
||||
MOZ_AIRBAG=,
|
||||
MOZ_AIRBAG=1)
|
||||
|
||||
if test -n "$MOZ_AIRBAG"; then
|
||||
AC_DEFINE(MOZ_AIRBAG)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable compilation of specific extension modules
|
||||
dnl ========================================================
|
||||
|
@ -7580,6 +7597,7 @@ AC_SUBST(MOZ_SINGLE_PROFILE)
|
|||
AC_SUBST(MOZ_SPELLCHECK)
|
||||
AC_SUBST(MOZ_XPFE_COMPONENTS)
|
||||
AC_SUBST(MOZ_USER_DIR)
|
||||
AC_SUBST(MOZ_AIRBAG)
|
||||
|
||||
AC_SUBST(ENABLE_STRIP)
|
||||
AC_SUBST(USE_ELF_DYNSTR_GC)
|
||||
|
|
|
@ -51,6 +51,10 @@ DIRS = \
|
|||
themes \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_AIRBAG
|
||||
DIRS += airbag
|
||||
endif
|
||||
|
||||
ifndef MINIMO
|
||||
DIRS += \
|
||||
xre \
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (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.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Airbag integration
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = airbag
|
||||
LIBXUL_LIBRARY = 1
|
||||
LIBRARY_NAME = airbagexception_s
|
||||
|
||||
REQUIRES = \
|
||||
xpcom \
|
||||
string \
|
||||
$(NULL)
|
||||
|
||||
DIRS = \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
DIRS += airbag/src/common/windows \
|
||||
airbag/src/client/windows \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
DIRS += client
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/airbag/src
|
||||
DEFINES += -DUNICODE -D_UNICODE
|
||||
|
||||
EXPORTS = \
|
||||
nsAirbagExceptionHandler.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsAirbagExceptionHandler.cpp \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,236 @@
|
|||
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.
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
# 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.
|
||||
|
||||
|
||||
# This allows #includes to be relative to src/
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
|
||||
|
||||
## Documentation
|
||||
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
|
||||
|
||||
dist_doc_DATA = \
|
||||
AUTHORS \
|
||||
COPYING \
|
||||
ChangeLog \
|
||||
INSTALL \
|
||||
NEWS \
|
||||
README
|
||||
|
||||
|
||||
## Libraries
|
||||
lib_LTLIBRARIES = src/libairbag.la
|
||||
|
||||
src_libairbag_la_SOURCES = \
|
||||
src/google/airbag_types.h \
|
||||
src/google/call_stack.h \
|
||||
src/google/minidump_processor.h \
|
||||
src/google/process_state.h \
|
||||
src/google/stack_frame.h \
|
||||
src/google/stack_frame_cpu.h \
|
||||
src/google/symbol_supplier.h \
|
||||
src/processor/address_map.h \
|
||||
src/processor/address_map-inl.h \
|
||||
src/processor/call_stack.cc \
|
||||
src/processor/contained_range_map.h \
|
||||
src/processor/contained_range_map-inl.h \
|
||||
src/processor/linked_ptr.h \
|
||||
src/processor/memory_region.h \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/minidump.h \
|
||||
src/processor/minidump_format.h \
|
||||
src/processor/minidump_processor.cc \
|
||||
src/processor/postfix_evaluator.h \
|
||||
src/processor/postfix_evaluator-inl.h \
|
||||
src/processor/process_state.cc \
|
||||
src/processor/range_map.h \
|
||||
src/processor/range_map-inl.h \
|
||||
src/processor/scoped_ptr.h \
|
||||
src/processor/source_line_resolver.cc \
|
||||
src/processor/source_line_resolver.h \
|
||||
src/processor/stack_frame_info.h \
|
||||
src/processor/stackwalker.cc \
|
||||
src/processor/stackwalker.h \
|
||||
src/processor/stackwalker_ppc.cc \
|
||||
src/processor/stackwalker_ppc.h \
|
||||
src/processor/stackwalker_x86.cc \
|
||||
src/processor/stackwalker_x86.h
|
||||
|
||||
|
||||
## Programs
|
||||
bin_PROGRAMS = \
|
||||
src/processor/minidump_dump \
|
||||
src/processor/minidump_stackwalk
|
||||
|
||||
|
||||
## Tests
|
||||
check_PROGRAMS = \
|
||||
src/processor/address_map_unittest \
|
||||
src/processor/contained_range_map_unittest \
|
||||
src/processor/minidump_processor_unittest \
|
||||
src/processor/postfix_evaluator_unittest \
|
||||
src/processor/range_map_unittest \
|
||||
src/processor/source_line_resolver_unittest
|
||||
|
||||
if SELFTEST
|
||||
check_PROGRAMS += \
|
||||
src/processor/stackwalker_selftest
|
||||
endif SELFTEST
|
||||
|
||||
check_SCRIPTS = \
|
||||
src/processor/minidump_dump_test \
|
||||
src/processor/minidump_stackwalk_test
|
||||
|
||||
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
||||
TESTS_ENVIRONMENT =
|
||||
|
||||
src_processor_address_map_unittest_SOURCES = \
|
||||
src/processor/address_map_unittest.cc
|
||||
|
||||
src_processor_contained_range_map_unittest_SOURCES = \
|
||||
src/processor/contained_range_map_unittest.cc
|
||||
|
||||
src_processor_minidump_processor_unittest_SOURCES = \
|
||||
src/processor/minidump_processor_unittest.cc
|
||||
src_processor_minidump_processor_unittest_LDADD = \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/process_state.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo \
|
||||
src/processor/source_line_resolver.lo
|
||||
|
||||
src_processor_postfix_evaluator_unittest_SOURCES = \
|
||||
src/processor/postfix_evaluator_unittest.cc
|
||||
|
||||
src_processor_range_map_unittest_SOURCES = \
|
||||
src/processor/range_map_unittest.cc
|
||||
|
||||
src_processor_source_line_resolver_unittest_SOURCES = \
|
||||
src/processor/source_line_resolver_unittest.cc
|
||||
src_processor_source_line_resolver_unittest_LDADD = \
|
||||
src/processor/source_line_resolver.lo
|
||||
|
||||
src_processor_stackwalker_selftest_SOURCES = \
|
||||
src/processor/stackwalker_selftest.cc
|
||||
src_processor_stackwalker_selftest_LDADD = \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/source_line_resolver.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
|
||||
## Non-installables
|
||||
noinst_PROGRAMS =
|
||||
noinst_SCRIPTS = $(check_SCRIPTS)
|
||||
|
||||
src_processor_minidump_dump_SOURCES = \
|
||||
src/processor/minidump_dump.cc
|
||||
src_processor_minidump_dump_LDADD = \
|
||||
src/processor/minidump.lo
|
||||
|
||||
src_processor_minidump_stackwalk_SOURCES = \
|
||||
src/processor/minidump_stackwalk.cc
|
||||
src_processor_minidump_stackwalk_LDADD = \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/stackwalker.lo \
|
||||
src/processor/stackwalker_ppc.lo \
|
||||
src/processor/stackwalker_x86.lo \
|
||||
src/processor/source_line_resolver.lo
|
||||
|
||||
|
||||
## Additional files to be included in a source distribution
|
||||
EXTRA_DIST = \
|
||||
$(SCRIPTS) \
|
||||
src/processor/testdata/minidump1.dmp \
|
||||
src/processor/testdata/minidump1.out \
|
||||
src/processor/testdata/minidump1.stack.out \
|
||||
src/processor/testdata/minidump2.dmp \
|
||||
src/processor/testdata/minidump2.sym \
|
||||
src/processor/testdata/module1.out \
|
||||
src/processor/testdata/module2.out \
|
||||
src/processor/testdata/module3_bad.out
|
||||
|
||||
|
||||
## Additional rules
|
||||
libtool: $(LIBTOOL_DEPS)
|
||||
$(SHELL) ./config.status --recheck
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
|||
Airbag is a set of client and server components which implement a
|
||||
crash-reporting system.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,530 @@
|
|||
#! /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:
|
|
@ -0,0 +1,323 @@
|
|||
#!/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:
|
|
@ -0,0 +1,360 @@
|
|||
#! /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:
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,70 @@
|
|||
# Copyright (c) 2006, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(airbag, 0.1, opensource@google.com)
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(autotools)
|
||||
|
||||
AM_INIT_AUTOMAKE(subdir-objects)
|
||||
AM_CONFIG_HEADER(src/config.h)
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXX
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
AC_SUBST(LIBTOOL_DEPS)
|
||||
|
||||
AC_HEADER_STDC
|
||||
|
||||
AC_ARG_ENABLE(selftest,
|
||||
AS_HELP_STRING([--enable-selftest],
|
||||
[Run extra tests with "make check" ]
|
||||
[(may conflict with optimizations) ]
|
||||
[(default is no)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
selftest=true
|
||||
;;
|
||||
no)
|
||||
selftest=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --enable-selftest)
|
||||
;;
|
||||
esac],
|
||||
[selftest=false])
|
||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
|
@ -0,0 +1,46 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (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.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Airbag integration
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = handler sender
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,58 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (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.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Airbag integration
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = handler
|
||||
LIBRARY_NAME = exception_handler
|
||||
XPI_NAME = crashreporter
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../../..
|
||||
DEFINES += -DUNICODE -D_UNICODE
|
||||
|
||||
CPPSRCS = \
|
||||
exception_handler.cc \
|
||||
$(NULL)
|
||||
|
||||
# need static lib
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,140 @@
|
|||
// 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 <ObjBase.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "client/windows/handler/exception_handler.h"
|
||||
#include "common/windows/guid_string.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
ExceptionHandler *ExceptionHandler::current_handler_ = NULL;
|
||||
|
||||
ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler)
|
||||
: callback_(callback), callback_context_(callback_context),
|
||||
dump_path_(dump_path), dbghelp_module_(NULL),
|
||||
minidump_write_dump_(NULL), previous_handler_(current_handler_),
|
||||
previous_filter_(NULL) {
|
||||
UpdateNextID();
|
||||
dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
|
||||
if (dbghelp_module_) {
|
||||
minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(
|
||||
GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
|
||||
}
|
||||
if (install_handler) {
|
||||
previous_filter_ = SetUnhandledExceptionFilter(HandleException);
|
||||
current_handler_ = this;
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionHandler::~ExceptionHandler() {
|
||||
if (dbghelp_module_) {
|
||||
FreeLibrary(dbghelp_module_);
|
||||
}
|
||||
if (current_handler_ == this) {
|
||||
SetUnhandledExceptionFilter(previous_filter_);
|
||||
current_handler_ = previous_handler_;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS *exinfo) {
|
||||
if (!current_handler_->WriteMinidumpWithException(exinfo)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
bool success = WriteMinidumpWithException(NULL);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context) {
|
||||
ExceptionHandler handler(dump_path, callback, callback_context, false);
|
||||
return handler.WriteMinidump();
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo) {
|
||||
wchar_t dump_file_name[MAX_PATH];
|
||||
swprintf_s(dump_file_name, MAX_PATH, L"%s\\%s.dmp",
|
||||
dump_path_.c_str(), next_minidump_id_.c_str());
|
||||
|
||||
bool success = false;
|
||||
if (minidump_write_dump_) {
|
||||
HANDLE dump_file = CreateFile(dump_file_name,
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (dump_file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
||||
except_info.ThreadId = GetCurrentThreadId();
|
||||
except_info.ExceptionPointers = exinfo;
|
||||
except_info.ClientPointers = FALSE;
|
||||
|
||||
// The explicit comparison to TRUE avoids a warning (C4800).
|
||||
success = (minidump_write_dump_(GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
dump_file,
|
||||
MiniDumpNormal,
|
||||
&except_info,
|
||||
NULL,
|
||||
NULL) == TRUE);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
callback_(next_minidump_id_, callback_context_, success);
|
||||
}
|
||||
// TODO(bryner): log an error on failure
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void ExceptionHandler::UpdateNextID() {
|
||||
GUID id;
|
||||
CoCreateGuid(&id);
|
||||
next_minidump_id_ = GUIDString::GUIDToWString(&id);
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,150 @@
|
|||
// 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.
|
||||
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
// or when WriteMinidump() is called explicitly by your program.
|
||||
//
|
||||
// To have the exception handler write minidumps when an uncaught exception
|
||||
// (crash) occurs, you should create an instance early in the execution
|
||||
// of your program, and keep it around for the entire time you want to
|
||||
// have crash handling active (typically, until shutdown).
|
||||
//
|
||||
// If you want to write minidumps without installing the exception handler,
|
||||
// you can create an ExceptionHandler with install_handler set to false,
|
||||
// then call WriteMinidump. You can also use this technique if you want to
|
||||
// use different minidump callbacks for different call sites.
|
||||
//
|
||||
// In either case, a callback function is called when a minidump is written,
|
||||
// which receives the unqiue id of the minidump. The caller can use this
|
||||
// id to collect and write additional application state, and to launch an
|
||||
// external crash-reporting application.
|
||||
//
|
||||
// It is important that creation and destruction of ExceptionHandler objects
|
||||
// be nested cleanly, when using install_handler = true.
|
||||
// Avoid the following pattern:
|
||||
// ExceptionHandler *e = new ExceptionHandler(...);
|
||||
// ExceptionHandler *f = new ExceptionHandler(...);
|
||||
// delete e;
|
||||
// This will put the exception filter stack into an inconsistent state.
|
||||
//
|
||||
// To use this library in your project, you will need to link against
|
||||
// ole32.lib.
|
||||
|
||||
#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
||||
#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
||||
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
|
||||
#pragma warning( push )
|
||||
// disable exception handler warnings
|
||||
#pragma warning( disable : 4530 )
|
||||
#include <string>
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::wstring;
|
||||
|
||||
class ExceptionHandler {
|
||||
public:
|
||||
// A callback function to run after the minidump has been written.
|
||||
// minidump_id is a unique id for the dump, so the minidump
|
||||
// file is <dump_path>\<minidump_id>.dmp. succeeded indicates whether
|
||||
// a minidump file was successfully written.
|
||||
typedef void (*MinidumpCallback)(const wstring &minidump_id,
|
||||
void *context, bool succeeded);
|
||||
|
||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
||||
// Minidump files will be written to dump_path, and the optional callback
|
||||
// is called after writing the dump file, as described above.
|
||||
// If install_handler is true, then a minidump will be written whenever
|
||||
// an unhandled exception occurs. If it is false, minidumps will only
|
||||
// be written when WriteMinidump is called.
|
||||
ExceptionHandler(const wstring &dump_path, MinidumpCallback callback,
|
||||
void *callback_context, bool install_handler);
|
||||
~ExceptionHandler();
|
||||
|
||||
// Get and set the minidump path
|
||||
wstring dump_path() const { return dump_path_; }
|
||||
void set_dump_path(const wstring &dump_path) { dump_path_ = dump_path; }
|
||||
|
||||
// Writes a minidump immediately. This can be used to capture the
|
||||
// execution state independently of a crash. Returns true on success.
|
||||
bool WriteMinidump();
|
||||
|
||||
// Convenience form of WriteMinidump which does not require an
|
||||
// ExceptionHandler instance.
|
||||
static bool WriteMinidump(const wstring &dump_path,
|
||||
MinidumpCallback callback, void *callback_context);
|
||||
|
||||
private:
|
||||
// Function pointer type for MiniDumpWriteDump, which is looked up
|
||||
// dynamically.
|
||||
typedef BOOL (WINAPI *MiniDumpWriteDump_type)(
|
||||
HANDLE hProcess,
|
||||
DWORD dwPid,
|
||||
HANDLE hFile,
|
||||
MINIDUMP_TYPE DumpType,
|
||||
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||
|
||||
// This function does the actual writing of a minidump.
|
||||
bool WriteMinidumpWithException(EXCEPTION_POINTERS *exinfo);
|
||||
|
||||
// Called when an unhandled exception occurs.
|
||||
static LONG WINAPI HandleException(EXCEPTION_POINTERS *exinfo);
|
||||
|
||||
// Generates a new ID and stores it in next_minidump_id_.
|
||||
void UpdateNextID();
|
||||
|
||||
MinidumpCallback callback_;
|
||||
void *callback_context_;
|
||||
|
||||
wstring dump_path_;
|
||||
wstring next_minidump_id_;
|
||||
|
||||
HMODULE dbghelp_module_;
|
||||
MiniDumpWriteDump_type minidump_write_dump_;
|
||||
|
||||
ExceptionHandler *previous_handler_; // current_handler_ before us
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_;
|
||||
|
||||
// the currently-installed ExceptionHandler, of which there can be only 1
|
||||
static ExceptionHandler *current_handler_;
|
||||
|
||||
// disallow copy ctor and operator=
|
||||
explicit ExceptionHandler(const ExceptionHandler &);
|
||||
void operator=(const ExceptionHandler &);
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#pragma warning( pop )
|
||||
#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
|
@ -0,0 +1,307 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="exception_handler"
|
||||
ProjectGUID="{B55CA863-B374-4BAF-95AC-539E4FA4C90C}"
|
||||
RootNamespace="exception_handler"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\.."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\.."
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="DebugStaticCRT|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\.."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="ReleaseStaticCRT|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="4"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\.."
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLibrarianTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\exception_handler.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\guid_string.cc"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\exception_handler.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\guid_string.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
|
@ -0,0 +1,57 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (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.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Airbag integration
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = sender
|
||||
LIBRARY_NAME = crash_report_sender
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../../..
|
||||
DEFINES += -DUNICODE -D_UNICODE
|
||||
|
||||
CPPSRCS = \
|
||||
crash_report_sender.cc \
|
||||
$(NULL)
|
||||
|
||||
# need static lib
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,45 @@
|
|||
// 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.
|
||||
|
||||
#pragma warning( disable : 4530 )
|
||||
#include "client/windows/sender/crash_report_sender.h"
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
// static
|
||||
bool CrashReportSender::SendCrashReport(
|
||||
const wstring &url, const map<wstring, wstring> ¶meters,
|
||||
const wstring &dump_file_name) {
|
||||
|
||||
return HTTPUpload::SendRequest(url, parameters, dump_file_name,
|
||||
L"upload_file_minidump");
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,77 @@
|
|||
// 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_airbag {
|
||||
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
|
||||
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. Returns true on success.
|
||||
// TODO(bryner): we should expose the response to the caller.
|
||||
static bool SendCrashReport(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const wstring &dump_file_name);
|
||||
|
||||
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_airbag
|
||||
|
||||
#pragma warning( pop )
|
||||
#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__
|
|
@ -0,0 +1,58 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (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.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla Airbag integration
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = airbag_common
|
||||
LIBRARY_NAME = airbag_common_s
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../..
|
||||
DEFINES += -DUNICODE -D_UNICODE
|
||||
|
||||
CPPSRCS = \
|
||||
guid_string.cc \
|
||||
http_upload.cc \
|
||||
$(NULL)
|
||||
|
||||
# need static lib
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,52 @@
|
|||
// 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.
|
||||
|
||||
// guid_string.cc: Convert GUIDs to strings.
|
||||
//
|
||||
// See guid_string.h for documentation.
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "common/windows/guid_string.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
// static
|
||||
wstring GUIDString::GUIDToWString(GUID *guid) {
|
||||
wchar_t guid_string[37];
|
||||
_snwprintf_s(guid_string, sizeof(guid_string) / sizeof(wchar_t), _TRUNCATE,
|
||||
L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
guid->Data1, guid->Data2, guid->Data3,
|
||||
guid->Data4[0], guid->Data4[1], guid->Data4[2],
|
||||
guid->Data4[3], guid->Data4[4], guid->Data4[5],
|
||||
guid->Data4[6], guid->Data4[7]);
|
||||
return wstring(guid_string);
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,52 @@
|
|||
// 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.
|
||||
|
||||
// guid_string.cc: Convert GUIDs to strings.
|
||||
|
||||
#ifndef COMMON_WINDOWS_GUID_STRING_H__
|
||||
#define COMMON_WINDOWS_GUID_STRING_H__
|
||||
|
||||
#include <Guiddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::wstring;
|
||||
|
||||
class GUIDString {
|
||||
public:
|
||||
// Converts guid to a string in the format recommended by RFC 4122 and
|
||||
// returns the string.
|
||||
static wstring GUIDToWString(GUID *guid);
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // COMMON_WINDOWS_GUID_STRING_H__
|
|
@ -0,0 +1,288 @@
|
|||
// 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 <assert.h>
|
||||
#include <Windows.h>
|
||||
#include <WinInet.h>
|
||||
|
||||
#pragma warning( disable : 4530 )
|
||||
#include <fstream>
|
||||
|
||||
#include "common/windows/http_upload.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::ifstream;
|
||||
using std::ios;
|
||||
|
||||
static const wchar_t kUserAgent[] = L"Airbag/1.0 (Windows)";
|
||||
|
||||
// Helper class which closes an internet handle when it goes away
|
||||
class HTTPUpload::AutoInternetHandle {
|
||||
public:
|
||||
explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
|
||||
~AutoInternetHandle() {
|
||||
if (handle_) {
|
||||
InternetCloseHandle(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
HINTERNET get() { return handle_; }
|
||||
|
||||
private:
|
||||
HINTERNET handle_;
|
||||
};
|
||||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const wstring &upload_file,
|
||||
const wstring &file_part_name) {
|
||||
// TODO(bryner): support non-ASCII parameter names
|
||||
if (!CheckParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Break up the URL and make sure we can handle it
|
||||
wchar_t scheme[16], host[256], path[256];
|
||||
URL_COMPONENTS components;
|
||||
memset(&components, 0, sizeof(components));
|
||||
components.dwStructSize = sizeof(components);
|
||||
components.lpszScheme = scheme;
|
||||
components.dwSchemeLength = sizeof(scheme);
|
||||
components.lpszHostName = host;
|
||||
components.dwHostNameLength = sizeof(host);
|
||||
components.lpszUrlPath = path;
|
||||
components.dwUrlPathLength = sizeof(path);
|
||||
if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
|
||||
0, &components)) {
|
||||
return false;
|
||||
}
|
||||
bool secure = false;
|
||||
if (wcscmp(scheme, L"https") == 0) {
|
||||
secure = true;
|
||||
} else if (wcscmp(scheme, L"http") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoInternetHandle internet(InternetOpen(kUserAgent,
|
||||
INTERNET_OPEN_TYPE_PRECONFIG,
|
||||
NULL, // proxy name
|
||||
NULL, // proxy bypass
|
||||
0)); // flags
|
||||
if (!internet.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoInternetHandle connection(InternetConnect(internet.get(),
|
||||
host,
|
||||
components.nPort,
|
||||
NULL, // user name
|
||||
NULL, // password
|
||||
INTERNET_SERVICE_HTTP,
|
||||
0, // flags
|
||||
NULL)); // context
|
||||
if (!connection.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
|
||||
AutoInternetHandle request(HttpOpenRequest(connection.get(),
|
||||
L"POST",
|
||||
path,
|
||||
NULL, // version
|
||||
NULL, // referer
|
||||
NULL, // agent type
|
||||
http_open_flags,
|
||||
NULL)); // context
|
||||
if (!request.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring boundary = GenerateMultipartBoundary();
|
||||
wstring content_type_header = GenerateRequestHeader(boundary);
|
||||
HttpAddRequestHeaders(request.get(),
|
||||
content_type_header.c_str(),
|
||||
-1, HTTP_ADDREQ_FLAG_ADD);
|
||||
|
||||
string request_body;
|
||||
GenerateRequestBody(parameters, upload_file,
|
||||
file_part_name, boundary, &request_body);
|
||||
|
||||
if (!HttpSendRequest(request.get(), NULL, 0,
|
||||
const_cast<char *>(request_body.data()),
|
||||
static_cast<DWORD>(request_body.size()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The server indicates a successful upload with HTTP status 200.
|
||||
wchar_t http_status[4];
|
||||
DWORD http_status_size = sizeof(http_status);
|
||||
if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
|
||||
static_cast<LPVOID>(&http_status), &http_status_size,
|
||||
0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (wcscmp(http_status, L"200") == 0);
|
||||
}
|
||||
|
||||
// static
|
||||
wstring HTTPUpload::GenerateMultipartBoundary() {
|
||||
// The boundary has 27 '-' characters followed by 16 hex digits
|
||||
static const wchar_t kBoundaryPrefix[] = L"---------------------------";
|
||||
static const int kBoundaryLength = 27 + 16 + 1;
|
||||
|
||||
// Generate some random numbers to fill out the boundary
|
||||
int r0 = rand();
|
||||
int r1 = rand();
|
||||
|
||||
wchar_t temp[kBoundaryLength];
|
||||
swprintf_s(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
|
||||
return wstring(temp);
|
||||
}
|
||||
|
||||
// static
|
||||
wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
|
||||
wstring header = L"Content-Type: multipart/form-data; boundary=";
|
||||
header += boundary;
|
||||
return header;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const wstring &upload_file,
|
||||
const wstring &file_part_name,
|
||||
const wstring &boundary,
|
||||
string *request_body) {
|
||||
vector<char> contents;
|
||||
GetFileContents(upload_file, &contents);
|
||||
if (contents.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string boundary_str = WideToUTF8(boundary);
|
||||
if (boundary_str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->clear();
|
||||
|
||||
// Append each of the parameter pairs as a form-data part
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append("Content-Disposition: form-data; name=\"" +
|
||||
WideToUTF8(pos->first) + "\"\r\n\r\n" +
|
||||
WideToUTF8(pos->second) + "\r\n");
|
||||
}
|
||||
|
||||
// Now append the upload file as a binary (octet-stream) part
|
||||
string filename_utf8 = WideToUTF8(upload_file);
|
||||
if (filename_utf8.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string file_part_name_utf8 = WideToUTF8(file_part_name);
|
||||
if (file_part_name_utf8.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
request_body->append("--" + boundary_str + "\r\n");
|
||||
request_body->append("Content-Disposition: form-data; "
|
||||
"name=\"" + file_part_name_utf8 + "\"; "
|
||||
"filename=\"" + filename_utf8 + "\"\r\n");
|
||||
request_body->append("Content-Type: application/octet-stream\r\n");
|
||||
request_body->append("\r\n");
|
||||
|
||||
request_body->append(&(contents[0]), contents.size());
|
||||
request_body->append("\r\n");
|
||||
request_body->append("--" + boundary_str + "--\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void HTTPUpload::GetFileContents(const wstring &filename,
|
||||
vector<char> *contents) {
|
||||
ifstream file;
|
||||
file.open(filename.c_str(), ios::binary);
|
||||
if (file.is_open()) {
|
||||
file.seekg(0, ios::end);
|
||||
int length = file.tellg();
|
||||
contents->resize(length);
|
||||
file.seekg(0, ios::beg);
|
||||
file.read(&((*contents)[0]), length);
|
||||
file.close();
|
||||
} else {
|
||||
contents->clear();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
string HTTPUpload::WideToUTF8(const wstring &wide) {
|
||||
if (wide.length() == 0) {
|
||||
return string();
|
||||
}
|
||||
|
||||
// compute the length of the buffer we'll need
|
||||
int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1,
|
||||
NULL, 0, NULL, NULL);
|
||||
if (charcount == 0) {
|
||||
return string();
|
||||
}
|
||||
|
||||
// convert
|
||||
char *buf = new char[charcount];
|
||||
WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount,
|
||||
NULL, NULL);
|
||||
|
||||
string result(buf);
|
||||
delete[] buf;
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckParameters(const map<wstring, wstring> ¶meters) {
|
||||
for (map<wstring, wstring>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
const wstring &str = pos->first;
|
||||
if (str.size() == 0) {
|
||||
return false; // disallow empty parameter names
|
||||
}
|
||||
for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
wchar_t c = str[i];
|
||||
if (c < 32 || c == '"' || c > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,107 @@
|
|||
// 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.
|
||||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using wininet. It currently supports requests that contain
|
||||
// a set of string parameters (key/value pairs), and a file to upload.
|
||||
|
||||
#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H__
|
||||
#define COMMON_WINDOWS_HTTP_UPLOAD_H__
|
||||
|
||||
#pragma warning( push )
|
||||
// disable exception handler warnings
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends the given set of parameters, along with the contents of
|
||||
// upload_file, as a multipart POST request to the given URL.
|
||||
// file_part_name contains the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// TODO(bryner): we should expose the response to the caller.
|
||||
static bool SendRequest(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const wstring &upload_file,
|
||||
const wstring &file_part_name);
|
||||
|
||||
private:
|
||||
class AutoInternetHandle;
|
||||
|
||||
// Generates a new multipart boundary for a POST request
|
||||
static wstring GenerateMultipartBoundary();
|
||||
|
||||
// Generates a HTTP request header for a multipart form submit.
|
||||
static wstring GenerateRequestHeader(const wstring &boundary);
|
||||
|
||||
// Given a set of parameters, an upload filename, and a file part name,
|
||||
// generates a multipart request body string with these parameters
|
||||
// and minidump contents. Returns true on success.
|
||||
static bool GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
const wstring &upload_file,
|
||||
const wstring &file_part_name,
|
||||
const wstring &boundary,
|
||||
string *request_body);
|
||||
|
||||
// Fills the supplied vector with the contents of filename.
|
||||
static void GetFileContents(const wstring &filename, vector<char> *contents);
|
||||
|
||||
// Converts a UTF16 string to UTF8.
|
||||
static string WideToUTF8(const wstring &wide);
|
||||
|
||||
// Checks that the given list of parameters has only printable
|
||||
// ASCII characters in the parameter name, and does not contain
|
||||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<wstring, wstring> ¶meters);
|
||||
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
explicit HTTPUpload(const HTTPUpload &);
|
||||
void operator=(const HTTPUpload &);
|
||||
~HTTPUpload();
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#pragma warning( pop )
|
||||
#endif // COMMON_WINDOWS_HTTP_UPLOAD_H__
|
|
@ -0,0 +1,639 @@
|
|||
// 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 <atlbase.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <dia2.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
#include "common/windows/guid_string.h"
|
||||
|
||||
// This constant may be missing from DbgHelp.h. See the documentation for
|
||||
// IDiaSymbol::get_undecoratedNameEx.
|
||||
#ifndef UNDNAME_NO_ECSU
|
||||
#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union.
|
||||
#endif // UNDNAME_NO_ECSU
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
|
||||
}
|
||||
|
||||
PDBSourceLineWriter::~PDBSourceLineWriter() {
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
|
||||
Close();
|
||||
|
||||
if (FAILED(CoInitialize(NULL))) {
|
||||
fprintf(stderr, "CoInitialize failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CComPtr<IDiaDataSource> data_source;
|
||||
if (FAILED(data_source.CoCreateInstance(CLSID_DiaSource))) {
|
||||
fprintf(stderr, "CoCreateInstance CLSID_DiaSource failed "
|
||||
"(msdia80.dll unregistered?)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case PDB_FILE:
|
||||
if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
|
||||
fprintf(stderr, "loadDataFromPdb failed\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EXE_FILE:
|
||||
if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
|
||||
fprintf(stderr, "loadDataForExe failed\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown file format\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FAILED(data_source->openSession(&session_))) {
|
||||
fprintf(stderr, "openSession failed\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
|
||||
// The line number format is:
|
||||
// <rva> <line number> <source file id>
|
||||
CComPtr<IDiaLineNumber> line;
|
||||
ULONG count;
|
||||
|
||||
while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) {
|
||||
DWORD rva;
|
||||
if (FAILED(line->get_relativeVirtualAddress(&rva))) {
|
||||
fprintf(stderr, "failed to get line rva\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD length;
|
||||
if (FAILED(line->get_length(&length))) {
|
||||
fprintf(stderr, "failed to get line code length\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD source_id;
|
||||
if (FAILED(line->get_sourceFileId(&source_id))) {
|
||||
fprintf(stderr, "failed to get line source file id\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD line_num;
|
||||
if (FAILED(line->get_lineNumber(&line_num))) {
|
||||
fprintf(stderr, "failed to get line number\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(output_, "%x %x %d %d\n", rva, length, line_num, source_id);
|
||||
line.Release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) {
|
||||
// The function format is:
|
||||
// FUNC <address> <length> <param_stack_size> <function>
|
||||
DWORD rva;
|
||||
if (FAILED(function->get_relativeVirtualAddress(&rva))) {
|
||||
fprintf(stderr, "couldn't get rva\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ULONGLONG length;
|
||||
if (FAILED(function->get_length(&length))) {
|
||||
fprintf(stderr, "failed to get function length\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CComBSTR name;
|
||||
int stack_param_size;
|
||||
if (!GetSymbolFunctionName(function, &name, &stack_param_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the decorated name didn't give the parameter size, try to
|
||||
// calculate it.
|
||||
if (stack_param_size < 0) {
|
||||
stack_param_size = GetFunctionStackParamSize(function);
|
||||
}
|
||||
|
||||
fprintf(output_, "FUNC %x %llx %x %ws\n",
|
||||
rva, length, stack_param_size, name);
|
||||
|
||||
CComPtr<IDiaEnumLineNumbers> lines;
|
||||
if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PrintLines(lines)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintSourceFiles() {
|
||||
CComPtr<IDiaSymbol> global;
|
||||
if (FAILED(session_->get_globalScope(&global))) {
|
||||
fprintf(stderr, "get_globalScope failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CComPtr<IDiaEnumSymbols> compilands;
|
||||
if (FAILED(global->findChildren(SymTagCompiland, NULL,
|
||||
nsNone, &compilands))) {
|
||||
fprintf(stderr, "findChildren failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CComPtr<IDiaSymbol> compiland;
|
||||
ULONG count;
|
||||
while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
|
||||
CComPtr<IDiaEnumSourceFiles> source_files;
|
||||
if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) {
|
||||
return false;
|
||||
}
|
||||
CComPtr<IDiaSourceFile> file;
|
||||
while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) {
|
||||
DWORD file_id;
|
||||
if (FAILED(file->get_uniqueId(&file_id))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CComBSTR file_name;
|
||||
if (FAILED(file->get_fileName(&file_name))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fwprintf(output_, L"FILE %d %s\n", file_id, file_name);
|
||||
file.Release();
|
||||
}
|
||||
compiland.Release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintFunctions() {
|
||||
CComPtr<IDiaEnumSymbolsByAddr> symbols;
|
||||
if (FAILED(session_->getSymbolsByAddr(&symbols))) {
|
||||
fprintf(stderr, "failed to get symbol enumerator\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CComPtr<IDiaSymbol> symbol;
|
||||
if (FAILED(symbols->symbolByAddr(1, 0, &symbol))) {
|
||||
fprintf(stderr, "failed to enumerate symbols\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD rva_last = 0;
|
||||
if (FAILED(symbol->get_relativeVirtualAddress(&rva_last))) {
|
||||
fprintf(stderr, "failed to get symbol rva\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ULONG count;
|
||||
do {
|
||||
DWORD tag;
|
||||
if (FAILED(symbol->get_symTag(&tag))) {
|
||||
fprintf(stderr, "failed to get symbol tag\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// For a given function, DIA seems to give either a symbol with
|
||||
// SymTagFunction or SymTagPublicSymbol, but not both. This means
|
||||
// that PDBSourceLineWriter will output either a FUNC or PUBLIC line,
|
||||
// but not both.
|
||||
if (tag == SymTagFunction) {
|
||||
if (!PrintFunction(symbol)) {
|
||||
return false;
|
||||
}
|
||||
} else if (tag == SymTagPublicSymbol) {
|
||||
if (!PrintCodePublicSymbol(symbol)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
symbol.Release();
|
||||
} while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintFrameData() {
|
||||
// It would be nice if it were possible to output frame data alongside the
|
||||
// associated function, as is done with line numbers, but the DIA API
|
||||
// doesn't make it possible to get the frame data in that way.
|
||||
|
||||
CComPtr<IDiaEnumTables> tables;
|
||||
if (FAILED(session_->getEnumTables(&tables)))
|
||||
return false;
|
||||
|
||||
// Pick up the first table that supports IDiaEnumFrameData.
|
||||
CComPtr<IDiaEnumFrameData> frame_data_enum;
|
||||
CComPtr<IDiaTable> table;
|
||||
ULONG count;
|
||||
while (!frame_data_enum &&
|
||||
SUCCEEDED(tables->Next(1, &table, &count)) &&
|
||||
count == 1) {
|
||||
table->QueryInterface(_uuidof(IDiaEnumFrameData),
|
||||
reinterpret_cast<void**>(&frame_data_enum));
|
||||
table.Release();
|
||||
}
|
||||
if (!frame_data_enum)
|
||||
return false;
|
||||
|
||||
CComPtr<IDiaFrameData> frame_data;
|
||||
while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) &&
|
||||
count == 1) {
|
||||
DWORD type;
|
||||
if (FAILED(frame_data->get_type(&type)))
|
||||
return false;
|
||||
|
||||
DWORD rva;
|
||||
if (FAILED(frame_data->get_relativeVirtualAddress(&rva)))
|
||||
return false;
|
||||
|
||||
DWORD code_size;
|
||||
if (FAILED(frame_data->get_lengthBlock(&code_size)))
|
||||
return false;
|
||||
|
||||
DWORD prolog_size;
|
||||
if (FAILED(frame_data->get_lengthProlog(&prolog_size)))
|
||||
return false;
|
||||
|
||||
// epliog_size is always 0.
|
||||
DWORD epilog_size = 0;
|
||||
|
||||
// parameter_size is the size of parameters passed on the stack. If any
|
||||
// parameters are not passed on the stack (such as in registers), their
|
||||
// sizes will not be included in parameter_size.
|
||||
DWORD parameter_size;
|
||||
if (FAILED(frame_data->get_lengthParams(¶meter_size)))
|
||||
return false;
|
||||
|
||||
DWORD saved_register_size;
|
||||
if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size)))
|
||||
return false;
|
||||
|
||||
DWORD local_size;
|
||||
if (FAILED(frame_data->get_lengthLocals(&local_size)))
|
||||
return false;
|
||||
|
||||
// get_maxStack can return S_FALSE, just use 0 in that case.
|
||||
DWORD max_stack_size = 0;
|
||||
if (FAILED(frame_data->get_maxStack(&max_stack_size)))
|
||||
return false;
|
||||
|
||||
// get_programString can return S_FALSE, indicating that there is no
|
||||
// program string. In that case, check whether %ebp is used.
|
||||
HRESULT program_string_result;
|
||||
CComBSTR program_string;
|
||||
if (FAILED(program_string_result = frame_data->get_program(
|
||||
&program_string))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get_allocatesBasePointer can return S_FALSE, treat that as though
|
||||
// %ebp is not used.
|
||||
BOOL allocates_base_pointer = FALSE;
|
||||
if (program_string_result != S_OK) {
|
||||
if (FAILED(frame_data->get_allocatesBasePointer(
|
||||
&allocates_base_pointer))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %d ",
|
||||
type, rva, code_size, prolog_size, epilog_size,
|
||||
parameter_size, saved_register_size, local_size, max_stack_size,
|
||||
program_string_result == S_OK);
|
||||
if (program_string_result == S_OK) {
|
||||
fprintf(output_, "%ws\n", program_string);
|
||||
} else {
|
||||
fprintf(output_, "%d\n", allocates_base_pointer);
|
||||
}
|
||||
|
||||
frame_data.Release();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
|
||||
BOOL is_code;
|
||||
if (FAILED(symbol->get_code(&is_code))) {
|
||||
return false;
|
||||
}
|
||||
if (!is_code) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD rva;
|
||||
if (FAILED(symbol->get_relativeVirtualAddress(&rva))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CComBSTR name;
|
||||
int stack_param_size;
|
||||
if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(output_, "PUBLIC %x %x %ws\n", rva,
|
||||
stack_param_size > 0 ? stack_param_size : 0, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
// wcstol_positive_strict is sort of like wcstol, but much stricter. string
|
||||
// should be a buffer pointing to a null-terminated string containing only
|
||||
// decimal digits. If the entire string can be converted to an integer
|
||||
// without overflowing, and there are no non-digit characters before the
|
||||
// result is set to the value and this function returns true. Otherwise,
|
||||
// this function returns false. This is an alternative to the strtol, atoi,
|
||||
// and scanf families, which are not as strict about input and in some cases
|
||||
// don't provide a good way for the caller to determine if a conversion was
|
||||
// successful.
|
||||
static bool wcstol_positive_strict(wchar_t *string, int *result) {
|
||||
int value = 0;
|
||||
for (wchar_t *c = string; *c != '\0'; ++c) {
|
||||
int last_value = value;
|
||||
value *= 10;
|
||||
// Detect overflow.
|
||||
if (value / 10 != last_value || value < 0) {
|
||||
return false;
|
||||
}
|
||||
if (*c < '0' || *c > '9') {
|
||||
return false;
|
||||
}
|
||||
unsigned int c_value = *c - '0';
|
||||
last_value = value;
|
||||
value += c_value;
|
||||
// Detect overflow.
|
||||
if (value < last_value) {
|
||||
return false;
|
||||
}
|
||||
// Forbid leading zeroes unless the string is just "0".
|
||||
if (value == 0 && *(c+1) != '\0') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
|
||||
BSTR *name,
|
||||
int *stack_param_size) {
|
||||
*stack_param_size = -1;
|
||||
const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS |
|
||||
UNDNAME_NO_FUNCTION_RETURNS |
|
||||
UNDNAME_NO_ALLOCATION_MODEL |
|
||||
UNDNAME_NO_ALLOCATION_LANGUAGE |
|
||||
UNDNAME_NO_THISTYPE |
|
||||
UNDNAME_NO_ACCESS_SPECIFIERS |
|
||||
UNDNAME_NO_THROW_SIGNATURES |
|
||||
UNDNAME_NO_MEMBER_TYPE |
|
||||
UNDNAME_NO_RETURN_UDT_MODEL |
|
||||
UNDNAME_NO_ECSU;
|
||||
|
||||
// Use get_undecoratedNameEx to get readable C++ names with arguments.
|
||||
if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) {
|
||||
if (function->get_name(name) != S_OK) {
|
||||
fprintf(stderr, "failed to get function name\n");
|
||||
return false;
|
||||
}
|
||||
// If a name comes from get_name because no undecorated form existed,
|
||||
// it's already formatted properly to be used as output. Don't do any
|
||||
// additional processing.
|
||||
} else {
|
||||
// C++ uses a bogus "void" argument for functions and methods that don't
|
||||
// take any parameters. Take it out of the undecorated name because it's
|
||||
// ugly and unnecessary.
|
||||
const wchar_t *replace_string = L"(void)";
|
||||
const size_t replace_length = wcslen(replace_string);
|
||||
const wchar_t *replacement_string = L"()";
|
||||
size_t length = wcslen(*name);
|
||||
if (length >= replace_length) {
|
||||
wchar_t *name_end = *name + length - replace_length;
|
||||
if (wcscmp(name_end, replace_string) == 0) {
|
||||
wcscpy_s(name_end, replace_length, replacement_string);
|
||||
length = wcslen(*name);
|
||||
}
|
||||
}
|
||||
|
||||
// Undecorate names used for stdcall and fastcall. These names prefix
|
||||
// the identifier with '_' (stdcall) or '@' (fastcall) and suffix it
|
||||
// with '@' followed by the number of bytes of parameters, in decimal.
|
||||
// If such a name is found, take note of the size and undecorate it.
|
||||
// Only do this for names that aren't C++, which is determined based on
|
||||
// whether the undecorated name contains any ':' or '(' characters.
|
||||
if (!wcschr(*name, ':') && !wcschr(*name, '(') &&
|
||||
(*name[0] == '_' || *name[0] == '@')) {
|
||||
wchar_t *last_at = wcsrchr(*name + 1, '@');
|
||||
if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) {
|
||||
// If this function adheres to the fastcall convention, it accepts up
|
||||
// to the first 8 bytes of parameters in registers (%ecx and %edx).
|
||||
// We're only interested in the stack space used for parameters, so
|
||||
// so subtract 8 and don't let the size go below 0.
|
||||
if (*name[0] == '@') {
|
||||
if (*stack_param_size > 8) {
|
||||
*stack_param_size -= 8;
|
||||
} else {
|
||||
*stack_param_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Undecorate the name by moving it one character to the left in its
|
||||
// buffer, and terminating it where the last '@' had been.
|
||||
wcsncpy_s(*name, length, *name + 1, last_at - *name - 1);
|
||||
} else if (*name[0] == '_') {
|
||||
// This symbol's name is encoded according to the cdecl rules. The
|
||||
// name doesn't end in a '@' character followed by a decimal positive
|
||||
// integer, so it's not a stdcall name. Strip off the leading
|
||||
// underscore.
|
||||
wcsncpy_s(*name, length, *name + 1, length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) {
|
||||
// This implementation is highly x86-specific.
|
||||
|
||||
// Gather the symbols corresponding to data.
|
||||
CComPtr<IDiaEnumSymbols> data_children;
|
||||
if (FAILED(function->findChildren(SymTagData, NULL, nsNone,
|
||||
&data_children))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// lowest_base is the lowest %ebp-relative byte offset used for a parameter.
|
||||
// highest_end is one greater than the highest offset (i.e. base + length).
|
||||
// Stack parameters are assumed to be contiguous, because in reality, they
|
||||
// are.
|
||||
int lowest_base = INT_MAX;
|
||||
int highest_end = INT_MIN;
|
||||
|
||||
CComPtr<IDiaSymbol> child;
|
||||
DWORD count;
|
||||
while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) {
|
||||
// If any operation fails at this point, just proceed to the next child.
|
||||
// Use the next_child label instead of continue because child needs to
|
||||
// be released before it's reused. Declare constructable/destructable
|
||||
// types early to avoid gotos that cross initializations.
|
||||
CComPtr<IDiaSymbol> child_type;
|
||||
|
||||
// DataIsObjectPtr is only used for |this|. Because |this| can be passed
|
||||
// as a stack parameter, look for it in addition to traditional
|
||||
// parameters.
|
||||
DWORD child_kind;
|
||||
if (FAILED(child->get_dataKind(&child_kind)) ||
|
||||
(child_kind != DataIsParam && child_kind != DataIsObjectPtr)) {
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
// Only concentrate on register-relative parameters. Parameters may also
|
||||
// be enregistered (passed directly in a register), but those don't
|
||||
// consume any stack space, so they're not of interest.
|
||||
DWORD child_location_type;
|
||||
if (FAILED(child->get_locationType(&child_location_type)) ||
|
||||
child_location_type != LocIsRegRel) {
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
// Of register-relative parameters, the only ones that make any sense are
|
||||
// %ebp- or %esp-relative. Note that MSVC's debugging information always
|
||||
// gives parameters as %ebp-relative even when a function doesn't use a
|
||||
// traditional frame pointer and stack parameters are accessed relative to
|
||||
// %esp, so just look for %ebp-relative parameters. If you wanted to
|
||||
// access parameters, you'd probably want to treat these %ebp-relative
|
||||
// offsets as if they were relative to %esp before a function's prolog
|
||||
// executed.
|
||||
DWORD child_register;
|
||||
if (FAILED(child->get_registerId(&child_register)) ||
|
||||
child_register != CV_REG_EBP) {
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
LONG child_register_offset;
|
||||
if (FAILED(child->get_offset(&child_register_offset))) {
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
if (FAILED(child->get_type(&child_type))) {
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
ULONGLONG child_length;
|
||||
if (FAILED(child_type->get_length(&child_length))) {
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
int child_end = child_register_offset + static_cast<ULONG>(child_length);
|
||||
if (child_register_offset < lowest_base) {
|
||||
lowest_base = child_register_offset;
|
||||
}
|
||||
if (child_end > highest_end) {
|
||||
highest_end = child_end;
|
||||
}
|
||||
|
||||
next_child:
|
||||
child.Release();
|
||||
}
|
||||
|
||||
int param_size = 0;
|
||||
// Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest
|
||||
// possible address to find a stack parameter before executing a function's
|
||||
// prolog (see above). Some optimizations cause parameter offsets to be
|
||||
// lower than 4, but we're not concerned with those because we're only
|
||||
// looking for parameters contained in addresses higher than where the
|
||||
// return address is stored.
|
||||
if (lowest_base < 4) {
|
||||
lowest_base = 4;
|
||||
}
|
||||
if (highest_end > lowest_base) {
|
||||
// All stack parameters are pushed as at least 4-byte quantities. If the
|
||||
// last type was narrower than 4 bytes, promote it. This assumes that all
|
||||
// parameters' offsets are 4-byte-aligned, which is always the case. Only
|
||||
// worry about the last type, because we're not summing the type sizes,
|
||||
// just looking at the lowest and highest offsets.
|
||||
int remainder = highest_end % 4;
|
||||
if (remainder) {
|
||||
highest_end += 4 - remainder;
|
||||
}
|
||||
|
||||
param_size = highest_end - lowest_base;
|
||||
}
|
||||
|
||||
return param_size;
|
||||
}
|
||||
|
||||
bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
|
||||
bool ret = false;
|
||||
output_ = map_file;
|
||||
if (PrintSourceFiles() && PrintFunctions() && PrintFrameData()) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
output_ = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PDBSourceLineWriter::Close() {
|
||||
session_.Release();
|
||||
}
|
||||
|
||||
wstring PDBSourceLineWriter::GetModuleGUID() {
|
||||
CComPtr<IDiaSymbol> global;
|
||||
if (FAILED(session_->get_globalScope(&global))) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
GUID guid;
|
||||
if (FAILED(global->get_guid(&guid))) {
|
||||
return L"";
|
||||
}
|
||||
|
||||
return GUIDString::GUIDToWString(&guid);
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,131 @@
|
|||
// 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.
|
||||
|
||||
// PDBSourceLineWriter uses a pdb file produced by Visual C++ to output
|
||||
// a line/address map for use with SourceLineResolver.
|
||||
|
||||
#ifndef _PDB_SOURCE_LINE_WRITER_H__
|
||||
#define _PDB_SOURCE_LINE_WRITER_H__
|
||||
|
||||
#include <atlcomcli.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
struct IDiaEnumLineNumbers;
|
||||
struct IDiaSession;
|
||||
struct IDiaSymbol;
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::wstring;
|
||||
|
||||
class PDBSourceLineWriter {
|
||||
public:
|
||||
enum FileFormat {
|
||||
PDB_FILE, // a .pdb file containing debug symbols
|
||||
EXE_FILE, // a .exe or .dll file
|
||||
};
|
||||
|
||||
explicit PDBSourceLineWriter();
|
||||
~PDBSourceLineWriter();
|
||||
|
||||
// Opens the given file. For executable files, the corresponding pdb
|
||||
// file must be available; Open will be if it is not.
|
||||
// If there is already a pdb file open, it is automatically closed.
|
||||
// Returns true on success.
|
||||
bool Open(const wstring &file, FileFormat format);
|
||||
|
||||
// Locates the pdb file for the given executable (exe or dll) file,
|
||||
// and opens it. If there is already a pdb file open, it is automatically
|
||||
// closed. Returns true on success.
|
||||
bool OpenExecutable(const wstring &exe_file);
|
||||
|
||||
// Writes a map file from the current pdb file to the given file stream.
|
||||
// Returns true on success.
|
||||
bool WriteMap(FILE *map_file);
|
||||
|
||||
// Closes the current pdb file and its associated resources.
|
||||
void Close();
|
||||
|
||||
// Returns the GUID for the module, as a string,
|
||||
// e.g. "11111111-2222-3333-4444-555555555555".
|
||||
wstring GetModuleGUID();
|
||||
|
||||
private:
|
||||
// Outputs the line/address pairs for each line in the enumerator.
|
||||
// Returns true on success.
|
||||
bool PrintLines(IDiaEnumLineNumbers *lines);
|
||||
|
||||
// Outputs a function address and name, followed by its source line list.
|
||||
// Returns true on success.
|
||||
bool PrintFunction(IDiaSymbol *function);
|
||||
|
||||
// Outputs all functions as described above. Returns true on success.
|
||||
bool PrintFunctions();
|
||||
|
||||
// Outputs all of the source files in the session's pdb file.
|
||||
// Returns true on success.
|
||||
bool PrintSourceFiles();
|
||||
|
||||
// Outputs all of the frame information necessary to construct stack
|
||||
// backtraces in the absence of frame pointers. Returns true on success.
|
||||
bool PrintFrameData();
|
||||
|
||||
// Outputs a single public symbol address and name, if the symbol corresponds
|
||||
// to a code address. Returns true on success. If symbol is does not
|
||||
// correspond to code, returns true without outputting anything.
|
||||
bool PrintCodePublicSymbol(IDiaSymbol *symbol);
|
||||
|
||||
// Returns the function name for a symbol. If possible, the name is
|
||||
// undecorated. If the symbol's decorated form indicates the size of
|
||||
// parameters on the stack, this information is returned in stack_param_size.
|
||||
// Returns true on success. If the symbol doesn't encode parameter size
|
||||
// information, stack_param_size is set to -1.
|
||||
static bool GetSymbolFunctionName(IDiaSymbol *function, BSTR *name,
|
||||
int *stack_param_size);
|
||||
|
||||
// Returns the number of bytes of stack space used for a function's
|
||||
// parameters. function must have the tag SymTagFunction. In the event of
|
||||
// a failure, returns 0, which is also a valid number of bytes.
|
||||
static int GetFunctionStackParamSize(IDiaSymbol *function);
|
||||
|
||||
// The session for the currently-open pdb file.
|
||||
CComPtr<IDiaSession> session_;
|
||||
|
||||
// The current output file for this WriteMap invocation.
|
||||
FILE *output_;
|
||||
|
||||
// Disallow copy ctor and operator=
|
||||
PDBSourceLineWriter(const PDBSourceLineWriter&);
|
||||
void operator=(const PDBSourceLineWriter&);
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // _PDB_SOURCE_LINE_WRITER_H__
|
|
@ -0,0 +1,86 @@
|
|||
// 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_airbag {
|
||||
|
||||
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_airbag
|
||||
|
||||
#endif // PROCESSOR_ADDRESS_MAP_INL_H__
|
|
@ -0,0 +1,80 @@
|
|||
// 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_airbag {
|
||||
|
||||
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_airbag
|
||||
|
||||
#endif // PROCESSOR_ADDRESS_MAP_H__
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
// 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_unittest.cc: Unit tests for AddressMap.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
|
||||
#include "processor/address_map-inl.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
|
||||
#define ASSERT_TRUE(condition) \
|
||||
if (!(condition)) { \
|
||||
fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
|
||||
|
||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
||||
|
||||
using google_airbag::AddressMap;
|
||||
using google_airbag::linked_ptr;
|
||||
|
||||
// A CountedObject holds an int. A global (not thread safe!) count of
|
||||
// allocated CountedObjects is maintained to help test memory management.
|
||||
class CountedObject {
|
||||
public:
|
||||
explicit CountedObject(int id) : id_(id) { ++count_; }
|
||||
~CountedObject() { --count_; }
|
||||
|
||||
static int count() { return count_; }
|
||||
int id() const { return id_; }
|
||||
|
||||
private:
|
||||
static int count_;
|
||||
int id_;
|
||||
};
|
||||
|
||||
int CountedObject::count_;
|
||||
|
||||
typedef int AddressType;
|
||||
typedef AddressMap< AddressType, linked_ptr<CountedObject> > TestMap;
|
||||
|
||||
static bool DoAddressMapTest() {
|
||||
ASSERT_EQ(CountedObject::count(), 0);
|
||||
|
||||
TestMap test_map;
|
||||
linked_ptr<CountedObject> entry;
|
||||
AddressType address;
|
||||
|
||||
// Check that a new map is truly empty.
|
||||
ASSERT_FALSE(test_map.Retrieve(0, &entry, &address));
|
||||
ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address));
|
||||
ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address));
|
||||
|
||||
// Check that Clear clears the map without leaking.
|
||||
ASSERT_EQ(CountedObject::count(), 0);
|
||||
ASSERT_TRUE(test_map.Store(1,
|
||||
linked_ptr<CountedObject>(new CountedObject(0))));
|
||||
ASSERT_TRUE(test_map.Retrieve(1, &entry, &address));
|
||||
ASSERT_EQ(CountedObject::count(), 1);
|
||||
test_map.Clear();
|
||||
ASSERT_EQ(CountedObject::count(), 1); // still holding entry in this scope
|
||||
|
||||
// Check that a cleared map is truly empty.
|
||||
ASSERT_FALSE(test_map.Retrieve(0, &entry, &address));
|
||||
ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address));
|
||||
ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address));
|
||||
|
||||
// Check a single-element map.
|
||||
ASSERT_TRUE(test_map.Store(10,
|
||||
linked_ptr<CountedObject>(new CountedObject(1))));
|
||||
ASSERT_FALSE(test_map.Retrieve(9, &entry, &address));
|
||||
ASSERT_TRUE(test_map.Retrieve(10, &entry, &address));
|
||||
ASSERT_EQ(CountedObject::count(), 1);
|
||||
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.
|
||||
ASSERT_TRUE(test_map.Store(5,
|
||||
linked_ptr<CountedObject>(new CountedObject(2))));
|
||||
ASSERT_EQ(CountedObject::count(), 2);
|
||||
ASSERT_TRUE(test_map.Store(20,
|
||||
linked_ptr<CountedObject>(new CountedObject(3))));
|
||||
ASSERT_TRUE(test_map.Store(15,
|
||||
linked_ptr<CountedObject>(new CountedObject(4))));
|
||||
ASSERT_FALSE(test_map.Store(10,
|
||||
linked_ptr<CountedObject>(new CountedObject(5)))); // already in map
|
||||
ASSERT_TRUE(test_map.Store(16,
|
||||
linked_ptr<CountedObject>(new CountedObject(6))));
|
||||
ASSERT_TRUE(test_map.Store(14,
|
||||
linked_ptr<CountedObject>(new CountedObject(7))));
|
||||
|
||||
// Nothing was stored with a key under 5. Don't use ASSERT inside loops
|
||||
// because it won't show exactly which key/entry/address failed.
|
||||
for (AddressType key = 0; key < 5; ++key) {
|
||||
if (test_map.Retrieve(key, &entry, &address)) {
|
||||
fprintf(stderr,
|
||||
"FAIL: retrieve %d expected false observed true @ %s:%d\n",
|
||||
key, __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check everything that was stored.
|
||||
const int id_verify[] = { 0, 0, 0, 0, 0, // unused
|
||||
2, 2, 2, 2, 2, // 5 - 9
|
||||
1, 1, 1, 1, 7, // 10 - 14
|
||||
4, 6, 6, 6, 6, // 15 - 19
|
||||
3, 3, 3, 3, 3, // 20 - 24
|
||||
3, 3, 3, 3, 3 }; // 25 - 29
|
||||
const AddressType address_verify[] = { 0, 0, 0, 0, 0, // unused
|
||||
5, 5, 5, 5, 5, // 5 - 9
|
||||
10, 10, 10, 10, 14, // 10 - 14
|
||||
15, 16, 16, 16, 16, // 15 - 19
|
||||
20, 20, 20, 20, 20, // 20 - 24
|
||||
20, 20, 20, 20, 20 }; // 25 - 29
|
||||
|
||||
for (AddressType key = 5; key < 30; ++key) {
|
||||
if (!test_map.Retrieve(key, &entry, &address)) {
|
||||
fprintf(stderr,
|
||||
"FAIL: retrieve %d expected true observed false @ %s:%d\n",
|
||||
key, __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
if (entry->id() != id_verify[key]) {
|
||||
fprintf(stderr,
|
||||
"FAIL: retrieve %d expected entry %d observed %d @ %s:%d\n",
|
||||
key, id_verify[key], entry->id(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
if (address != address_verify[key]) {
|
||||
fprintf(stderr,
|
||||
"FAIL: retrieve %d expected address %d observed %d @ %s:%d\n",
|
||||
key, address_verify[key], address, __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The stored objects should still be in the map.
|
||||
ASSERT_EQ(CountedObject::count(), 6);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool RunTests() {
|
||||
if (!DoAddressMapTest())
|
||||
return false;
|
||||
|
||||
// Leak check.
|
||||
ASSERT_EQ(CountedObject::count(), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// 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.
|
||||
|
||||
// call_stack.cc: A call stack comprised of stack frames.
|
||||
//
|
||||
// See call_stack.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
CallStack::~CallStack() {
|
||||
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
|
||||
iterator != frames_.end();
|
||||
++iterator) {
|
||||
delete *iterator;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,172 @@
|
|||
// 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_airbag {
|
||||
|
||||
|
||||
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_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
|
|
@ -0,0 +1,146 @@
|
|||
// 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_airbag {
|
||||
|
||||
|
||||
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_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_CONTAINED_RANGE_MAP_H__
|
|
@ -0,0 +1,253 @@
|
|||
// 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_unittest.cc: Unit tests for ContainedRangeMap
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "processor/contained_range_map-inl.h"
|
||||
|
||||
|
||||
#define ASSERT_TRUE(condition) \
|
||||
if (!(condition)) { \
|
||||
fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
|
||||
|
||||
|
||||
using google_airbag::ContainedRangeMap;
|
||||
|
||||
|
||||
static bool RunTests() {
|
||||
ContainedRangeMap<unsigned int, int> crm;
|
||||
|
||||
// First, do the StoreRange tests. This validates the containment
|
||||
// rules.
|
||||
ASSERT_TRUE (crm.StoreRange(10, 10, 1));
|
||||
ASSERT_FALSE(crm.StoreRange(10, 10, 2)); // exactly equal to 1
|
||||
ASSERT_FALSE(crm.StoreRange(11, 10, 3)); // begins inside 1 and extends up
|
||||
ASSERT_FALSE(crm.StoreRange( 9, 10, 4)); // begins below 1 and ends inside
|
||||
ASSERT_TRUE (crm.StoreRange(11, 9, 5)); // contained by existing
|
||||
ASSERT_TRUE (crm.StoreRange(12, 7, 6));
|
||||
ASSERT_TRUE (crm.StoreRange( 9, 12, 7)); // contains existing
|
||||
ASSERT_TRUE (crm.StoreRange( 9, 13, 8));
|
||||
ASSERT_TRUE (crm.StoreRange( 8, 14, 9));
|
||||
ASSERT_TRUE (crm.StoreRange(30, 3, 10));
|
||||
ASSERT_TRUE (crm.StoreRange(33, 3, 11));
|
||||
ASSERT_TRUE (crm.StoreRange(30, 6, 12)); // storable but totally masked
|
||||
ASSERT_TRUE (crm.StoreRange(40, 8, 13)); // will be totally masked
|
||||
ASSERT_TRUE (crm.StoreRange(40, 4, 14));
|
||||
ASSERT_TRUE (crm.StoreRange(44, 4, 15));
|
||||
ASSERT_FALSE(crm.StoreRange(32, 10, 16)); // begins in #10, ends in #14
|
||||
ASSERT_FALSE(crm.StoreRange(50, 0, 17)); // zero length
|
||||
ASSERT_TRUE (crm.StoreRange(50, 10, 18));
|
||||
ASSERT_TRUE (crm.StoreRange(50, 1, 19));
|
||||
ASSERT_TRUE (crm.StoreRange(59, 1, 20));
|
||||
ASSERT_TRUE (crm.StoreRange(60, 1, 21));
|
||||
ASSERT_TRUE (crm.StoreRange(69, 1, 22));
|
||||
ASSERT_TRUE (crm.StoreRange(60, 10, 23));
|
||||
ASSERT_TRUE (crm.StoreRange(68, 1, 24));
|
||||
ASSERT_TRUE (crm.StoreRange(61, 1, 25));
|
||||
ASSERT_TRUE (crm.StoreRange(61, 8, 26));
|
||||
ASSERT_FALSE(crm.StoreRange(59, 9, 27));
|
||||
ASSERT_FALSE(crm.StoreRange(59, 10, 28));
|
||||
ASSERT_FALSE(crm.StoreRange(59, 11, 29));
|
||||
ASSERT_TRUE (crm.StoreRange(70, 10, 30));
|
||||
ASSERT_TRUE (crm.StoreRange(74, 2, 31));
|
||||
ASSERT_TRUE (crm.StoreRange(77, 2, 32));
|
||||
ASSERT_FALSE(crm.StoreRange(72, 6, 33));
|
||||
ASSERT_TRUE (crm.StoreRange(80, 3, 34));
|
||||
ASSERT_TRUE (crm.StoreRange(81, 1, 35));
|
||||
ASSERT_TRUE (crm.StoreRange(82, 1, 36));
|
||||
ASSERT_TRUE (crm.StoreRange(83, 3, 37));
|
||||
ASSERT_TRUE (crm.StoreRange(84, 1, 38));
|
||||
ASSERT_TRUE (crm.StoreRange(83, 1, 39));
|
||||
ASSERT_TRUE (crm.StoreRange(86, 5, 40));
|
||||
ASSERT_TRUE (crm.StoreRange(88, 1, 41));
|
||||
ASSERT_TRUE (crm.StoreRange(90, 1, 42));
|
||||
ASSERT_TRUE (crm.StoreRange(86, 1, 43));
|
||||
ASSERT_TRUE (crm.StoreRange(87, 1, 44));
|
||||
ASSERT_TRUE (crm.StoreRange(89, 1, 45));
|
||||
ASSERT_TRUE (crm.StoreRange(87, 4, 46));
|
||||
ASSERT_TRUE (crm.StoreRange(87, 3, 47));
|
||||
ASSERT_FALSE(crm.StoreRange(86, 2, 48));
|
||||
|
||||
// Each element in test_data contains the expected result when calling
|
||||
// RetrieveRange on an address.
|
||||
const int test_data[] = {
|
||||
0, // 0
|
||||
0, // 1
|
||||
0, // 2
|
||||
0, // 3
|
||||
0, // 4
|
||||
0, // 5
|
||||
0, // 6
|
||||
0, // 7
|
||||
9, // 8
|
||||
7, // 9
|
||||
1, // 10
|
||||
5, // 11
|
||||
6, // 12
|
||||
6, // 13
|
||||
6, // 14
|
||||
6, // 15
|
||||
6, // 16
|
||||
6, // 17
|
||||
6, // 18
|
||||
5, // 19
|
||||
7, // 20
|
||||
8, // 21
|
||||
0, // 22
|
||||
0, // 23
|
||||
0, // 24
|
||||
0, // 25
|
||||
0, // 26
|
||||
0, // 27
|
||||
0, // 28
|
||||
0, // 29
|
||||
10, // 30
|
||||
10, // 31
|
||||
10, // 32
|
||||
11, // 33
|
||||
11, // 34
|
||||
11, // 35
|
||||
0, // 36
|
||||
0, // 37
|
||||
0, // 38
|
||||
0, // 39
|
||||
14, // 40
|
||||
14, // 41
|
||||
14, // 42
|
||||
14, // 43
|
||||
15, // 44
|
||||
15, // 45
|
||||
15, // 46
|
||||
15, // 47
|
||||
0, // 48
|
||||
0, // 49
|
||||
19, // 50
|
||||
18, // 51
|
||||
18, // 52
|
||||
18, // 53
|
||||
18, // 54
|
||||
18, // 55
|
||||
18, // 56
|
||||
18, // 57
|
||||
18, // 58
|
||||
20, // 59
|
||||
21, // 60
|
||||
25, // 61
|
||||
26, // 62
|
||||
26, // 63
|
||||
26, // 64
|
||||
26, // 65
|
||||
26, // 66
|
||||
26, // 67
|
||||
24, // 68
|
||||
22, // 69
|
||||
30, // 70
|
||||
30, // 71
|
||||
30, // 72
|
||||
30, // 73
|
||||
31, // 74
|
||||
31, // 75
|
||||
30, // 76
|
||||
32, // 77
|
||||
32, // 78
|
||||
30, // 79
|
||||
34, // 80
|
||||
35, // 81
|
||||
36, // 82
|
||||
39, // 83
|
||||
38, // 84
|
||||
37, // 85
|
||||
43, // 86
|
||||
44, // 87
|
||||
41, // 88
|
||||
45, // 89
|
||||
42, // 90
|
||||
0, // 91
|
||||
0, // 92
|
||||
0, // 93
|
||||
0, // 94
|
||||
0, // 95
|
||||
0, // 96
|
||||
0, // 97
|
||||
0, // 98
|
||||
0 // 99
|
||||
};
|
||||
unsigned int test_high = sizeof(test_data) / sizeof(int);
|
||||
|
||||
// Now, do the RetrieveRange tests. This further validates that the
|
||||
// objects were stored properly and that retrieval returns the correct
|
||||
// object.
|
||||
// If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a
|
||||
// new test_data array will be printed. Exercise caution when doing this.
|
||||
// Be sure to verify the results manually!
|
||||
#ifdef GENERATE_TEST_DATA
|
||||
printf(" const int test_data[] = {\n");
|
||||
#endif // GENERATE_TEST_DATA
|
||||
|
||||
for (unsigned int address = 0; address < test_high; ++address) {
|
||||
int value;
|
||||
if (!crm.RetrieveRange(address, &value))
|
||||
value = 0;
|
||||
|
||||
#ifndef GENERATE_TEST_DATA
|
||||
// Don't use ASSERT inside the loop because it won't show the failed
|
||||
// |address|, and the line number will always be the same. That makes
|
||||
// it difficult to figure out which test failed.
|
||||
if (value != test_data[address]) {
|
||||
fprintf(stderr, "FAIL: retrieve %d expected %d observed %d @ %s:%d\n",
|
||||
address, test_data[address], value, __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
#else // !GENERATE_TEST_DATA
|
||||
printf(" %d%c%s // %d\n", value,
|
||||
address == test_high - 1 ? ' ' : ',',
|
||||
value < 10 ? " " : "",
|
||||
address);
|
||||
#endif // !GENERATE_TEST_DATA
|
||||
}
|
||||
|
||||
#ifdef GENERATE_TEST_DATA
|
||||
printf(" };\n");
|
||||
#endif // GENERATE_TEST_DATA
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
// 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.
|
||||
|
||||
// A "smart" pointer type with reference tracking. Every pointer to a
|
||||
// particular object is kept on a circular linked list. When the last pointer
|
||||
// to an object is destroyed or reassigned, the object is deleted.
|
||||
//
|
||||
// Used properly, this deletes the object when the last reference goes away.
|
||||
// There are several caveats:
|
||||
// - Like all reference counting schemes, cycles lead to leaks.
|
||||
// - Each smart pointer is actually two pointers (8 bytes instead of 4).
|
||||
// - Every time a pointer is assigned, the entire list of pointers to that
|
||||
// object is traversed. This class is therefore NOT SUITABLE when there
|
||||
// will often be more than two or three pointers to a particular object.
|
||||
// - References are only tracked as long as linked_ptr<> objects are copied.
|
||||
// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
|
||||
// will happen (double deletion).
|
||||
//
|
||||
// A good use of this class is storing object references in STL containers.
|
||||
// You can safely put linked_ptr<> in a vector<>.
|
||||
// Other uses may not be as good.
|
||||
//
|
||||
// Note: If you use an incomplete type with linked_ptr<>, the class
|
||||
// *containing* linked_ptr<> must have a constructor and destructor (even
|
||||
// if they do nothing!).
|
||||
|
||||
#ifndef PROCESSOR_LINKED_PTR_H__
|
||||
#define PROCESSOR_LINKED_PTR_H__
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
// This is used internally by all instances of linked_ptr<>. It needs to be
|
||||
// a non-template class because different types of linked_ptr<> can refer to
|
||||
// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
|
||||
// So, it needs to be possible for different types of linked_ptr to participate
|
||||
// in the same circular linked list, so we need a single class type here.
|
||||
//
|
||||
// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
|
||||
class linked_ptr_internal {
|
||||
public:
|
||||
// Create a new circle that includes only this instance.
|
||||
void join_new() {
|
||||
next_ = this;
|
||||
}
|
||||
|
||||
// Join an existing circle.
|
||||
void join(linked_ptr_internal const* ptr) {
|
||||
linked_ptr_internal const* p = ptr;
|
||||
while (p->next_ != ptr) p = p->next_;
|
||||
p->next_ = this;
|
||||
next_ = ptr;
|
||||
}
|
||||
|
||||
// Leave whatever circle we're part of. Returns true iff we were the
|
||||
// last member of the circle. Once this is done, you can join() another.
|
||||
bool depart() {
|
||||
if (next_ == this) return true;
|
||||
linked_ptr_internal const* p = next_;
|
||||
while (p->next_ != this) p = p->next_;
|
||||
p->next_ = next_;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable linked_ptr_internal const* next_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class linked_ptr {
|
||||
public:
|
||||
typedef T element_type;
|
||||
|
||||
// Take over ownership of a raw pointer. This should happen as soon as
|
||||
// possible after the object is created.
|
||||
explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
|
||||
~linked_ptr() { depart(); }
|
||||
|
||||
// Copy an existing linked_ptr<>, adding ourselves to the list of references.
|
||||
template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
|
||||
linked_ptr(linked_ptr const& ptr) { copy(&ptr); }
|
||||
|
||||
// Assignment releases the old value and acquires the new.
|
||||
template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
|
||||
depart();
|
||||
copy(&ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
linked_ptr& operator=(linked_ptr const& ptr) {
|
||||
if (&ptr != this) {
|
||||
depart();
|
||||
copy(&ptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Smart pointer members.
|
||||
void reset(T* ptr = NULL) { depart(); capture(ptr); }
|
||||
T* get() const { return value_; }
|
||||
T* operator->() const { return value_; }
|
||||
T& operator*() const { return *value_; }
|
||||
// Release ownership of the pointed object and returns it.
|
||||
// Sole ownership by this linked_ptr object is required.
|
||||
T* release() {
|
||||
bool last = link_.depart();
|
||||
T* v = value_;
|
||||
value_ = NULL;
|
||||
return v;
|
||||
}
|
||||
|
||||
bool operator==(T* p) const { return value_ == p; }
|
||||
bool operator!=(T* p) const { return value_ != p; }
|
||||
template <typename U>
|
||||
bool operator==(linked_ptr<U> const& ptr) const {
|
||||
return value_ == ptr.get();
|
||||
}
|
||||
template <typename U>
|
||||
bool operator!=(linked_ptr<U> const& ptr) const {
|
||||
return value_ != ptr.get();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
friend class linked_ptr;
|
||||
|
||||
T* value_;
|
||||
linked_ptr_internal link_;
|
||||
|
||||
void depart() {
|
||||
if (link_.depart()) delete value_;
|
||||
}
|
||||
|
||||
void capture(T* ptr) {
|
||||
value_ = ptr;
|
||||
link_.join_new();
|
||||
}
|
||||
|
||||
template <typename U> void copy(linked_ptr<U> const* ptr) {
|
||||
value_ = ptr->get();
|
||||
if (value_)
|
||||
link_.join(&ptr->link_);
|
||||
else
|
||||
link_.join_new();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> inline
|
||||
bool operator==(T* ptr, const linked_ptr<T>& x) {
|
||||
return ptr == x.get();
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator!=(T* ptr, const linked_ptr<T>& x) {
|
||||
return ptr != x.get();
|
||||
}
|
||||
|
||||
// A function to convert T* into linked_ptr<T>
|
||||
// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
|
||||
// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
|
||||
template <typename T>
|
||||
linked_ptr<T> make_linked_ptr(T* ptr) {
|
||||
return linked_ptr<T>(ptr);
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // PROCESSOR_LINKED_PTR_H__
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// minidump_dump.cc: Print the contents of a minidump file in somewhat
|
||||
// readable text.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "processor/minidump.h"
|
||||
|
||||
|
||||
using std::string;
|
||||
using namespace google_airbag;
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Minidump minidump(argv[1]);
|
||||
if (!minidump.Read()) {
|
||||
printf("minidump.Read() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
minidump.Print();
|
||||
|
||||
int error = 0;
|
||||
|
||||
MinidumpThreadList* threadList = minidump.GetThreadList();
|
||||
if (!threadList) {
|
||||
error |= 1 << 2;
|
||||
printf("minidump.GetThreadList() failed\n");
|
||||
} else {
|
||||
threadList->Print();
|
||||
}
|
||||
|
||||
MinidumpModuleList* moduleList = minidump.GetModuleList();
|
||||
if (!moduleList) {
|
||||
error |= 1 << 3;
|
||||
printf("minidump.GetModuleList() failed\n");
|
||||
} else {
|
||||
moduleList->Print();
|
||||
}
|
||||
|
||||
MinidumpMemoryList* memoryList = minidump.GetMemoryList();
|
||||
if (!memoryList) {
|
||||
error |= 1 << 4;
|
||||
printf("minidump.GetMemoryList() failed\n");
|
||||
} else {
|
||||
memoryList->Print();
|
||||
}
|
||||
|
||||
MinidumpException* exception = minidump.GetException();
|
||||
if (!exception) {
|
||||
error |= 1 << 5;
|
||||
printf("minidump.GetException() failed\n");
|
||||
} else {
|
||||
exception->Print();
|
||||
}
|
||||
|
||||
MinidumpSystemInfo* systemInfo = minidump.GetSystemInfo();
|
||||
if (!systemInfo) {
|
||||
error |= 1 << 6;
|
||||
printf("minidump.GetSystemInfo() failed\n");
|
||||
} else {
|
||||
systemInfo->Print();
|
||||
}
|
||||
|
||||
MinidumpMiscInfo* miscInfo = minidump.GetMiscInfo();
|
||||
if (!miscInfo) {
|
||||
error |= 1 << 7;
|
||||
printf("minidump.GetMiscInfo() failed\n");
|
||||
} else {
|
||||
miscInfo->Print();
|
||||
}
|
||||
|
||||
// Use return instead of exit to allow destructors to run.
|
||||
return(error);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
testdata_dir=$srcdir/src/processor/testdata
|
||||
./src/processor/minidump_dump $testdata_dir/minidump1.dmp | \
|
||||
tr -s '\015' '\012' | \
|
||||
diff -u $testdata_dir/minidump1.out -
|
||||
exit $?
|
|
@ -0,0 +1,369 @@
|
|||
// 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 "google/minidump_processor.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/process_state.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier)
|
||||
: supplier_(supplier) {
|
||||
}
|
||||
|
||||
MinidumpProcessor::~MinidumpProcessor() {
|
||||
}
|
||||
|
||||
ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
||||
Minidump dump(minidump_file);
|
||||
if (!dump.Read()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scoped_ptr<ProcessState> process_state(new ProcessState());
|
||||
|
||||
process_state->cpu_ = GetCPUInfo(&dump, &process_state->cpu_info_);
|
||||
process_state->os_ = GetOSInfo(&dump, &process_state->os_version_);
|
||||
|
||||
u_int32_t exception_thread_id = 0;
|
||||
MinidumpException *exception = dump.GetException();
|
||||
if (exception) {
|
||||
process_state->crashed_ = true;
|
||||
exception_thread_id = exception->GetThreadID();
|
||||
|
||||
process_state->crash_reason_ = GetCrashReason(
|
||||
&dump, &process_state->crash_address_);
|
||||
}
|
||||
|
||||
MinidumpThreadList *threads = dump.GetThreadList();
|
||||
if (!threads) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool found_crash_thread = false;
|
||||
unsigned int thread_count = threads->thread_count();
|
||||
for (unsigned int thread_index = 0;
|
||||
thread_index < thread_count;
|
||||
++thread_index) {
|
||||
MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
|
||||
if (!thread) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (process_state->crashed_ &&
|
||||
thread->GetThreadID() == exception_thread_id) {
|
||||
if (found_crash_thread) {
|
||||
// There can't be more than one crash thread.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
process_state->crash_thread_ = thread_index;
|
||||
found_crash_thread = true;
|
||||
}
|
||||
|
||||
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
||||
if (!thread_memory) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scoped_ptr<Stackwalker> stackwalker(
|
||||
Stackwalker::StackwalkerForCPU(exception->GetContext(),
|
||||
thread_memory,
|
||||
dump.GetModuleList(),
|
||||
supplier_));
|
||||
if (!stackwalker.get()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scoped_ptr<CallStack> stack(stackwalker->Walk());
|
||||
if (!stack.get()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
process_state->threads_.push_back(stack.release());
|
||||
}
|
||||
|
||||
// If the process crashed, there must be a crash thread.
|
||||
if (process_state->crashed_ && !found_crash_thread) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return process_state.release();
|
||||
}
|
||||
|
||||
// Returns the MDRawSystemInfo from a minidump, or NULL if system info is
|
||||
// not available from the minidump. If system_info is non-NULL, it is used
|
||||
// to pass back the MinidumpSystemInfo object.
|
||||
static const MDRawSystemInfo* GetSystemInfo(Minidump *dump,
|
||||
MinidumpSystemInfo **system_info) {
|
||||
MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo();
|
||||
if (!minidump_system_info)
|
||||
return NULL;
|
||||
|
||||
if (system_info)
|
||||
*system_info = minidump_system_info;
|
||||
|
||||
return minidump_system_info->system_info();
|
||||
}
|
||||
|
||||
// static
|
||||
string MinidumpProcessor::GetCPUInfo(Minidump *dump, string *cpu_info) {
|
||||
if (cpu_info)
|
||||
cpu_info->clear();
|
||||
|
||||
MinidumpSystemInfo *system_info;
|
||||
const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info);
|
||||
if (!raw_system_info)
|
||||
return "";
|
||||
|
||||
string cpu;
|
||||
switch (raw_system_info->processor_architecture) {
|
||||
case MD_CPU_ARCHITECTURE_X86: {
|
||||
cpu = "x86";
|
||||
if (cpu_info) {
|
||||
const string *cpu_vendor = system_info->GetCPUVendor();
|
||||
if (cpu_vendor) {
|
||||
cpu_info->assign(*cpu_vendor);
|
||||
cpu_info->append(" ");
|
||||
}
|
||||
|
||||
char x86_info[36];
|
||||
snprintf(x86_info, sizeof(x86_info), "family %u model %u stepping %u",
|
||||
raw_system_info->processor_level,
|
||||
raw_system_info->processor_revision >> 8,
|
||||
raw_system_info->processor_revision & 0xff);
|
||||
cpu_info->append(x86_info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CPU_ARCHITECTURE_PPC: {
|
||||
cpu = "ppc";
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// Assign the numeric architecture ID into the CPU string.
|
||||
char cpu_string[7];
|
||||
snprintf(cpu_string, sizeof(cpu_string), "0x%04x",
|
||||
raw_system_info->processor_architecture);
|
||||
cpu = cpu_string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
// static
|
||||
string MinidumpProcessor::GetOSInfo(Minidump *dump, string *os_version) {
|
||||
if (os_version)
|
||||
os_version->clear();
|
||||
|
||||
MinidumpSystemInfo *system_info;
|
||||
const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info);
|
||||
if (!raw_system_info)
|
||||
return "";
|
||||
|
||||
string os;
|
||||
switch (raw_system_info->platform_id) {
|
||||
case MD_OS_WIN32_NT: {
|
||||
os = "Windows NT";
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_OS_WIN32_WINDOWS: {
|
||||
os = "Windows";
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_OS_MAC_OS_X: {
|
||||
os = "Mac OS X";
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_OS_LINUX: {
|
||||
os = "Linux";
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// Assign the numeric platform ID into the OS string.
|
||||
char os_string[11];
|
||||
snprintf(os_string, sizeof(os_string), "0x%08x",
|
||||
raw_system_info->platform_id);
|
||||
os = os_string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (os_version) {
|
||||
char os_version_string[33];
|
||||
snprintf(os_version_string, sizeof(os_version_string), "%u.%u.%u",
|
||||
raw_system_info->major_version,
|
||||
raw_system_info->minor_version,
|
||||
raw_system_info->build_number);
|
||||
os_version->assign(os_version_string);
|
||||
|
||||
const string *csd_version = system_info->GetCSDVersion();
|
||||
if (csd_version) {
|
||||
os_version->append(" ");
|
||||
os_version->append(*csd_version);
|
||||
}
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
// static
|
||||
string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
|
||||
MinidumpException *exception = dump->GetException();
|
||||
if (!exception)
|
||||
return "";
|
||||
|
||||
const MDRawExceptionStream *raw_exception = exception->exception();
|
||||
if (!raw_exception)
|
||||
return "";
|
||||
|
||||
if (address)
|
||||
*address = raw_exception->exception_record.exception_address;
|
||||
|
||||
// The reason value is OS-specific and possibly CPU-specific. Set up
|
||||
// sensible numeric defaults for the reason string in case we can't
|
||||
// map the codes to a string (because there's no system info, or because
|
||||
// it's an unrecognized platform, or because it's an unrecognized code.)
|
||||
char reason_string[24];
|
||||
snprintf(reason_string, sizeof(reason_string), "0x%08x / 0x%08x",
|
||||
raw_exception->exception_record.exception_code,
|
||||
raw_exception->exception_record.exception_flags);
|
||||
string reason = reason_string;
|
||||
|
||||
const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, NULL);
|
||||
if (!raw_system_info)
|
||||
return reason;
|
||||
|
||||
switch (raw_system_info->platform_id) {
|
||||
case MD_OS_WIN32_NT:
|
||||
case MD_OS_WIN32_WINDOWS: {
|
||||
switch (raw_exception->exception_record.exception_code) {
|
||||
case MD_EXCEPTION_CODE_WIN_CONTROL_C:
|
||||
reason = "DBG_CONTROL_C";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION:
|
||||
reason = "EXCEPTION_GUARD_PAGE";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT:
|
||||
reason = "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_BREAKPOINT:
|
||||
reason = "EXCEPTION_BREAKPOINT";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_SINGLE_STEP:
|
||||
reason = "EXCEPTION_SINGLE_STEP";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION:
|
||||
// For EXCEPTION_ACCESS_VIOLATION, Windows puts the address that
|
||||
// caused the fault in exception_information[1].
|
||||
// exception_information[0] is 0 if the violation was caused by
|
||||
// an attempt to read data and 1 if it was an attempt to write
|
||||
// data.
|
||||
// This information is useful in addition to the code address, which
|
||||
// will be present in the crash thread's instruction field anyway.
|
||||
reason = "EXCEPTION_ACCESS_VIOLATION";
|
||||
if (address &&
|
||||
raw_exception->exception_record.number_parameters >= 2) {
|
||||
*address =
|
||||
raw_exception->exception_record.exception_information[1];
|
||||
}
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR:
|
||||
reason = "EXCEPTION_IN_PAGE_ERROR";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_INVALID_HANDLE:
|
||||
reason = "EXCEPTION_INVALID_HANDLE";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION:
|
||||
reason = "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION:
|
||||
reason = "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION:
|
||||
reason = "EXCEPTION_INVALID_DISPOSITION";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED:
|
||||
reason = "EXCEPTION_BOUNDS_EXCEEDED";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND:
|
||||
reason = "EXCEPTION_FLT_DENORMAL_OPERAND";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO:
|
||||
reason = "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT:
|
||||
reason = "EXCEPTION_FLT_INEXACT_RESULT";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION:
|
||||
reason = "EXCEPTION_FLT_INVALID_OPERATION";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW:
|
||||
reason = "EXCEPTION_FLT_OVERFLOW";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK:
|
||||
reason = "EXCEPTION_FLT_STACK_CHECK";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW:
|
||||
reason = "EXCEPTION_FLT_UNDERFLOW";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO:
|
||||
reason = "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW:
|
||||
reason = "EXCEPTION_INT_OVERFLOW";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION:
|
||||
reason = "EXCEPTION_PRIV_INSTRUCTION";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW:
|
||||
reason = "EXCEPTION_STACK_OVERFLOW";
|
||||
break;
|
||||
case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK:
|
||||
reason = "EXCEPTION_POSSIBLE_DEADLOCK";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,135 @@
|
|||
// 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.
|
||||
|
||||
// Unit test for MinidumpProcessor. Uses a pre-generated minidump and
|
||||
// corresponding symbol file, and checks the stack frames for correctness.
|
||||
|
||||
#include <string>
|
||||
#include "google/call_stack.h"
|
||||
#include "google/minidump_processor.h"
|
||||
#include "google/process_state.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google/symbol_supplier.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
using std::string;
|
||||
using google_airbag::CallStack;
|
||||
using google_airbag::MinidumpProcessor;
|
||||
using google_airbag::ProcessState;
|
||||
using google_airbag::scoped_ptr;
|
||||
|
||||
#define ASSERT_TRUE(cond) \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
class TestSymbolSupplier : public SymbolSupplier {
|
||||
public:
|
||||
virtual string GetSymbolFile(MinidumpModule *module);
|
||||
};
|
||||
|
||||
string TestSymbolSupplier::GetSymbolFile(MinidumpModule *module) {
|
||||
if (*(module->GetName()) == "c:\\test_app.exe") {
|
||||
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata/minidump2.sym";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
using google_airbag::TestSymbolSupplier;
|
||||
|
||||
static bool RunTests() {
|
||||
TestSymbolSupplier supplier;
|
||||
MinidumpProcessor processor(&supplier);
|
||||
|
||||
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata/minidump2.dmp";
|
||||
|
||||
scoped_ptr<ProcessState> state(processor.Process(minidump_file));
|
||||
ASSERT_TRUE(state.get());
|
||||
ASSERT_EQ(state->cpu(), "x86");
|
||||
ASSERT_EQ(state->cpu_info(), "GenuineIntel family 6 model 13 stepping 8");
|
||||
ASSERT_EQ(state->os(), "Windows NT");
|
||||
ASSERT_EQ(state->os_version(), "5.1.2600 Service Pack 2");
|
||||
ASSERT_TRUE(state->crashed());
|
||||
ASSERT_EQ(state->crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
||||
ASSERT_EQ(state->crash_address(), 0);
|
||||
ASSERT_EQ(state->threads()->size(), 1);
|
||||
ASSERT_EQ(state->crash_thread(), 0);
|
||||
CallStack *stack = state->threads()->at(0);
|
||||
ASSERT_TRUE(stack);
|
||||
ASSERT_EQ(stack->frames()->size(), 4);
|
||||
|
||||
ASSERT_EQ(stack->frames()->at(0)->module_base, 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(0)->module_name, "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(0)->function_name, "CrashFunction()");
|
||||
ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
|
||||
ASSERT_EQ(stack->frames()->at(0)->source_line, 65);
|
||||
|
||||
ASSERT_EQ(stack->frames()->at(1)->module_base, 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(1)->module_name, "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
|
||||
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
|
||||
ASSERT_EQ(stack->frames()->at(1)->source_line, 70);
|
||||
|
||||
// This comes from the CRT
|
||||
ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(2)->module_name, "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
|
||||
ASSERT_EQ(stack->frames()->at(2)->source_file_name,
|
||||
"f:\\rtm\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c");
|
||||
ASSERT_EQ(stack->frames()->at(2)->source_line, 318);
|
||||
|
||||
// No debug info available for kernel32.dll
|
||||
ASSERT_EQ(stack->frames()->at(3)->module_base, 0x7c800000);
|
||||
ASSERT_EQ(stack->frames()->at(3)->module_name,
|
||||
"C:\\WINDOWS\\system32\\kernel32.dll");
|
||||
ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
|
||||
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
|
||||
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!RunTests()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// minidump_stackwalk.cc: Print the stack of the exception thread from a
|
||||
// minidump.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
||||
|
||||
using std::string;
|
||||
using google_airbag::CallStack;
|
||||
using google_airbag::MemoryRegion;
|
||||
using google_airbag::Minidump;
|
||||
using google_airbag::MinidumpContext;
|
||||
using google_airbag::MinidumpException;
|
||||
using google_airbag::MinidumpModuleList;
|
||||
using google_airbag::MinidumpThread;
|
||||
using google_airbag::MinidumpThreadList;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::StackFrame;
|
||||
using google_airbag::Stackwalker;
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Minidump minidump(argv[1]);
|
||||
if (!minidump.Read()) {
|
||||
fprintf(stderr, "minidump.Read() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpException *exception = minidump.GetException();
|
||||
if (!exception) {
|
||||
fprintf(stderr, "minidump.GetException() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||
if (!thread_list) {
|
||||
fprintf(stderr, "minidump.GetThreadList() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpThread *exception_thread =
|
||||
thread_list->GetThreadByID(exception->GetThreadID());
|
||||
if (!exception_thread) {
|
||||
fprintf(stderr, "thread_list->GetThreadByID() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MemoryRegion *stack_memory = exception_thread->GetMemory();
|
||||
if (!stack_memory) {
|
||||
fprintf(stderr, "exception_thread->GetStackMemory() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpContext *context = exception->GetContext();
|
||||
if (!context) {
|
||||
fprintf(stderr, "exception->GetContext() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MinidumpModuleList *modules = minidump.GetModuleList();
|
||||
if (!modules) {
|
||||
fprintf(stderr, "minidump.GetModuleList() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
scoped_ptr<Stackwalker> stackwalker(
|
||||
Stackwalker::StackwalkerForCPU(context, stack_memory, modules, NULL));
|
||||
if (!stackwalker.get()) {
|
||||
fprintf(stderr, "Stackwalker::StackwalkerForCPU failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
scoped_ptr<CallStack> stack(stackwalker->Walk());
|
||||
|
||||
unsigned int index;
|
||||
for (index = 0; index < stack->frames()->size(); ++index) {
|
||||
StackFrame *frame = stack->frames()->at(index);
|
||||
printf("[%2d] instruction = 0x%08llx \"%s\" + 0x%08llx\n",
|
||||
index,
|
||||
frame->instruction,
|
||||
frame->module_base ? frame->module_name.c_str() : "0x0",
|
||||
frame->instruction - frame->module_base);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
testdata_dir=$srcdir/src/processor/testdata
|
||||
./src/processor/minidump_stackwalk $testdata_dir/minidump1.dmp | \
|
||||
tr -s '\015' '\012' | \
|
||||
diff -u $testdata_dir/minidump1.stack.out -
|
||||
exit $?
|
|
@ -0,0 +1,241 @@
|
|||
// 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 "processor/memory_region.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
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_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_POSTFIX_EVALUATOR_INL_H__
|
|
@ -0,0 +1,145 @@
|
|||
// 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.h: Postfix (reverse Polish) notation expression evaluator.
|
||||
//
|
||||
// PostfixEvaluator evaluates an expression, using the expression itself
|
||||
// in postfix (reverse Polish) notation and a dictionary mapping constants
|
||||
// and variables to their values. The evaluator supports standard
|
||||
// arithmetic operations, assignment into variables, and when an optional
|
||||
// MemoryRange is provided, dereferencing. (Any unary key-to-value operation
|
||||
// may be used with a MemoryRange implementation that returns the appropriate
|
||||
// values, but PostfixEvaluator was written with dereferencing in mind.)
|
||||
//
|
||||
// The expression language is simple. Expressions are supplied as strings,
|
||||
// with operands and operators delimited by whitespace. Operands may be
|
||||
// either literal values suitable for ValueType, or constants or variables,
|
||||
// which reference the dictionary. The supported binary operators are +
|
||||
// (addition), - (subtraction), * (multiplication), / (quotient of division),
|
||||
// and % (modulus of division). The unary ^ (dereference) operator is also
|
||||
// provided. These operators allow any operand to be either a literal
|
||||
// value, constant, or variable. Assignment (=) of any type of operand into
|
||||
// a variable is also supported.
|
||||
//
|
||||
// The dictionary is provided as a map with string keys. Keys beginning
|
||||
// with the '$' character are treated as variables. All other keys are
|
||||
// treated as constants. Any results must be assigned into variables in the
|
||||
// dictionary. These variables do not need to exist prior to calling
|
||||
// Evaluate, unless used in an expression prior to being assigned to. The
|
||||
// internal stack state is not made available after evaluation, and any
|
||||
// values remaining on the stack are treated as evidence of incomplete
|
||||
// execution and cause the evaluator to indicate failure.
|
||||
//
|
||||
// PostfixEvaluator is intended to support evaluation of "program strings"
|
||||
// obtained from MSVC frame data debugging information in pdb files as
|
||||
// returned by the DIA APIs.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_POSTFIX_EVALUATOR_H__
|
||||
#define PROCESSOR_POSTFIX_EVALUATOR_H__
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "processor/memory_region.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
class MemoryRegion;
|
||||
|
||||
template<typename ValueType>
|
||||
class PostfixEvaluator {
|
||||
public:
|
||||
typedef map<string, ValueType> DictionaryType;
|
||||
typedef map<string, bool> DictionaryValidityType;
|
||||
|
||||
// Create a PostfixEvaluator object that may be used (with Evaluate) on
|
||||
// one or more expressions. PostfixEvaluator does not take ownership of
|
||||
// either argument. |memory| may be NULL, in which case dereferencing
|
||||
// (^) will not be supported. |dictionary| may be NULL, but evaluation
|
||||
// will fail in that case unless set_dictionary is used before calling
|
||||
// Evaluate.
|
||||
PostfixEvaluator(DictionaryType *dictionary, MemoryRegion *memory)
|
||||
: dictionary_(dictionary), memory_(memory), stack_() {}
|
||||
|
||||
// Evaluate the expression. The results of execution will be stored
|
||||
// in one (or more) variables in the dictionary. Returns false if any
|
||||
// failures occure during execution, leaving variables in the dictionary
|
||||
// in an indeterminate state. If assigned is non-NULL, any keys set in
|
||||
// the dictionary as a result of evaluation will also be set to true in
|
||||
// assigned, providing a way to determine if an expression modifies any
|
||||
// of its input variables.
|
||||
bool Evaluate(const string &expression, DictionaryValidityType *assigned);
|
||||
|
||||
DictionaryType* dictionary() const { return dictionary_; }
|
||||
|
||||
// Reset the dictionary. PostfixEvaluator does not take ownership.
|
||||
void set_dictionary(DictionaryType *dictionary) {dictionary_ = dictionary; }
|
||||
|
||||
private:
|
||||
// Return values for PopValueOrIdentifier
|
||||
enum PopResult {
|
||||
POP_RESULT_FAIL = 0,
|
||||
POP_RESULT_VALUE,
|
||||
POP_RESULT_IDENTIFIER
|
||||
};
|
||||
|
||||
// Retrieves the topmost literal value, constant, or variable from the
|
||||
// stack. Returns POP_RESULT_VALUE if the topmost entry is a literal
|
||||
// value, and sets |value| accordingly. Returns POP_RESULT_IDENTIFIER
|
||||
// if the topmost entry is a constant or variable identifier, and sets
|
||||
// |identifier| accordingly. Returns POP_RESULT_FAIL on failure, such
|
||||
// as when the stack is empty.
|
||||
PopResult PopValueOrIdentifier(ValueType *value, string *identifier);
|
||||
|
||||
// Retrieves the topmost value on the stack. If the topmost entry is
|
||||
// an identifier, the dictionary is queried for the identifier's value.
|
||||
// Returns false on failure, such as when the stack is empty or when
|
||||
// a nonexistent identifier is named.
|
||||
bool PopValue(ValueType *value);
|
||||
|
||||
// Retrieves the top two values on the stack, in the style of PopValue.
|
||||
// value2 is popped before value1, so that value1 corresponds to the
|
||||
// entry that was pushed prior to value2. Returns false on failure.
|
||||
bool PopValues(ValueType *value1, ValueType *value2);
|
||||
|
||||
// Pushes a new value onto the stack.
|
||||
void PushValue(const ValueType &value);
|
||||
|
||||
// The dictionary mapping constant and variable identifiers (strings) to
|
||||
// values. Keys beginning with '$' are treated as variable names, and
|
||||
// PostfixEvaluator is free to create and modify these keys. Weak pointer.
|
||||
DictionaryType *dictionary_;
|
||||
|
||||
// If non-NULL, the MemoryRegion used for dereference (^) operations.
|
||||
// If NULL, dereferencing is unsupported and will fail. Weak pointer.
|
||||
MemoryRegion *memory_;
|
||||
|
||||
// The stack contains state information as execution progresses. Values
|
||||
// are pushed on to it as the expression string is read and as operations
|
||||
// yield values; values are popped when used as operands to operators.
|
||||
vector<string> stack_;
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_POSTFIX_EVALUATOR_H__
|
|
@ -0,0 +1,279 @@
|
|||
// 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_unittest.cc: Unit tests for PostfixEvaluator.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "processor/memory_region.h"
|
||||
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using google_airbag::MemoryRegion;
|
||||
using google_airbag::PostfixEvaluator;
|
||||
|
||||
|
||||
// FakeMemoryRegion is used to test PostfixEvaluator's dereference (^)
|
||||
// operator. The result of dereferencing a value is one greater than
|
||||
// the value.
|
||||
class FakeMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
virtual u_int64_t GetBase() { return 0; }
|
||||
virtual u_int32_t GetSize() { return 0; }
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct EvaluateTest {
|
||||
// Expression passed to PostfixEvaluator::Evaluate.
|
||||
const string expression;
|
||||
|
||||
// True if the expression is expected to be evaluable, false if evaluation
|
||||
// is expected to fail.
|
||||
bool evaluable;
|
||||
};
|
||||
|
||||
|
||||
struct EvaluateTestSet {
|
||||
// The dictionary used for all tests in the set.
|
||||
PostfixEvaluator<unsigned int>::DictionaryType *dictionary;
|
||||
|
||||
// The list of tests.
|
||||
const EvaluateTest *evaluate_tests;
|
||||
|
||||
// The number of tests.
|
||||
unsigned int evaluate_test_count;
|
||||
|
||||
// Identifiers and their expected values upon completion of the Evaluate
|
||||
// tests in the set.
|
||||
map<string, unsigned int> *validate_data;
|
||||
};
|
||||
|
||||
|
||||
bool RunTests() {
|
||||
// The first test set checks the basic operations and failure modes.
|
||||
PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
|
||||
const EvaluateTest evaluate_tests_0[] = {
|
||||
{ "$rAdd 2 2 + =", true }, // $rAdd = 2 + 2 = 4
|
||||
{ "$rAdd $rAdd 2 + =", true }, // $rAdd = $rAdd + 2 = 6
|
||||
{ "$rAdd 2 $rAdd + =", true }, // $rAdd = 2 + $rAdd = 8
|
||||
{ "99", false }, // put some junk on the stack...
|
||||
{ "$rAdd2 2 2 + =", true }, // ...and make sure things still work
|
||||
{ "$rAdd2\t2\n2 + =", true }, // same but with different whitespace
|
||||
{ "$rAdd2 2 2 + = ", true }, // trailing whitespace
|
||||
{ " $rAdd2 2 2 + =", true }, // leading whitespace
|
||||
{ "$rAdd2 2 2 + =", true }, // extra whitespace
|
||||
{ "$T0 2 = +", false }, // too few operands for add
|
||||
{ "2 + =", false }, // too few operands for add
|
||||
{ "2 +", false }, // too few operands for add
|
||||
{ "+", false }, // too few operands for add
|
||||
{ "^", false }, // too few operands for dereference
|
||||
{ "=", false }, // too few operands for assignment
|
||||
{ "2 =", false }, // too few operands for assignment
|
||||
{ "2 2 + =", false }, // too few operands for assignment
|
||||
{ "2 2 =", false }, // can't assign into a literal
|
||||
{ "k 2 =", false }, // can't assign into a constant
|
||||
{ "2", false }, // leftover data on stack
|
||||
{ "2 2 +", false }, // leftover data on stack
|
||||
{ "$rAdd", false }, // leftover data on stack
|
||||
{ "0 $T1 0 0 + =", false }, // leftover data on stack
|
||||
{ "$T2 $T2 2 + =", false }, // can't operate on an undefined value
|
||||
{ "$rMul 9 6 * =", true }, // $rMul = 9 * 6 = 54
|
||||
{ "$rSub 9 6 - =", true }, // $rSub = 9 - 6 = 3
|
||||
{ "$rDivQ 9 6 / =", true }, // $rDivQ = 9 / 6 = 1
|
||||
{ "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3
|
||||
{ "$rDeref 9 ^ =", true } // $rDeref = ^9 = 10 (FakeMemoryRegion)
|
||||
};
|
||||
map<string, unsigned int> validate_data_0;
|
||||
validate_data_0["$rAdd"] = 8;
|
||||
validate_data_0["$rAdd2"] = 4;
|
||||
validate_data_0["$rSub"] = 3;
|
||||
validate_data_0["$rMul"] = 54;
|
||||
validate_data_0["$rDivQ"] = 1;
|
||||
validate_data_0["$rDivM"] = 3;
|
||||
validate_data_0["$rDeref"] = 10;
|
||||
|
||||
// The second test set simulates a couple of MSVC program strings.
|
||||
// The data is fudged a little bit because the tests use FakeMemoryRegion
|
||||
// instead of a real stack snapshot, but the program strings are real and
|
||||
// the implementation doesn't know or care that the data is not real.
|
||||
PostfixEvaluator<unsigned int>::DictionaryType dictionary_1;
|
||||
dictionary_1["$ebp"] = 0xbfff0010;
|
||||
dictionary_1["$eip"] = 0x10000000;
|
||||
dictionary_1["$esp"] = 0xbfff0000;
|
||||
dictionary_1[".cbSavedRegs"] = 4;
|
||||
dictionary_1[".cbParams"] = 4;
|
||||
dictionary_1[".raSearchStart"] = 0xbfff0020;
|
||||
const EvaluateTest evaluate_tests_1[] = {
|
||||
{ "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
|
||||
"$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", true },
|
||||
// Intermediate state: $T0 = 0xbfff0010, $eip = 0xbfff0015,
|
||||
// $ebp = 0xbfff0011, $esp = 0xbfff0018,
|
||||
// $L = 0xbfff000c, $P = 0xbfff001c
|
||||
{ "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
|
||||
"$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =",
|
||||
true },
|
||||
// Intermediate state: $T0 = 0xbfff0011, $eip = 0xbfff0016,
|
||||
// $ebp = 0xbfff0012, $esp = 0xbfff0019,
|
||||
// $L = 0xbfff000d, $P = 0xbfff001d,
|
||||
// $ebx = 0xbffefff6
|
||||
{ "$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = "
|
||||
"$esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = "
|
||||
"$ebx $T0 28 - ^ =",
|
||||
true }
|
||||
};
|
||||
map<string, unsigned int> validate_data_1;
|
||||
validate_data_1["$T0"] = 0xbfff0012;
|
||||
validate_data_1["$T1"] = 0xbfff0020;
|
||||
validate_data_1["$T2"] = 0xbfff0019;
|
||||
validate_data_1["$eip"] = 0xbfff0021;
|
||||
validate_data_1["$ebp"] = 0xbfff0012;
|
||||
validate_data_1["$esp"] = 0xbfff0024;
|
||||
validate_data_1["$L"] = 0xbfff000e;
|
||||
validate_data_1["$P"] = 0xbfff0028;
|
||||
validate_data_1["$ebx"] = 0xbffefff7;
|
||||
validate_data_1[".cbSavedRegs"] = 4;
|
||||
validate_data_1[".cbParams"] = 4;
|
||||
|
||||
EvaluateTestSet evaluate_test_sets[] = {
|
||||
{ &dictionary_0, evaluate_tests_0,
|
||||
sizeof(evaluate_tests_0) / sizeof(EvaluateTest), &validate_data_0 },
|
||||
{ &dictionary_1, evaluate_tests_1,
|
||||
sizeof(evaluate_tests_1) / sizeof(EvaluateTest), &validate_data_1 },
|
||||
};
|
||||
|
||||
unsigned int evaluate_test_set_count = sizeof(evaluate_test_sets) /
|
||||
sizeof(EvaluateTestSet);
|
||||
|
||||
FakeMemoryRegion fake_memory;
|
||||
PostfixEvaluator<unsigned int> postfix_evaluator =
|
||||
PostfixEvaluator<unsigned int>(NULL, &fake_memory);
|
||||
|
||||
for (unsigned int evaluate_test_set_index = 0;
|
||||
evaluate_test_set_index < evaluate_test_set_count;
|
||||
++evaluate_test_set_index) {
|
||||
EvaluateTestSet *evaluate_test_set =
|
||||
&evaluate_test_sets[evaluate_test_set_index];
|
||||
const EvaluateTest *evaluate_tests = evaluate_test_set->evaluate_tests;
|
||||
unsigned int evaluate_test_count = evaluate_test_set->evaluate_test_count;
|
||||
|
||||
// The same dictionary will be used for each test in the set. Earlier
|
||||
// tests can affect the state of the dictionary for later tests.
|
||||
postfix_evaluator.set_dictionary(evaluate_test_set->dictionary);
|
||||
|
||||
// Use a new validity dictionary for each test set.
|
||||
PostfixEvaluator<unsigned int>::DictionaryValidityType assigned;
|
||||
|
||||
for (unsigned int evaluate_test_index = 0;
|
||||
evaluate_test_index < evaluate_test_count;
|
||||
++evaluate_test_index) {
|
||||
const EvaluateTest *evaluate_test = &evaluate_tests[evaluate_test_index];
|
||||
|
||||
// Do the test.
|
||||
bool result = postfix_evaluator.Evaluate(evaluate_test->expression,
|
||||
&assigned);
|
||||
if (result != evaluate_test->evaluable) {
|
||||
fprintf(stderr, "FAIL: evaluate set %d/%d, test %d/%d, "
|
||||
"expression \"%s\", expected %s, observed %s\n",
|
||||
evaluate_test_set_index, evaluate_test_set_count,
|
||||
evaluate_test_index, evaluate_test_count,
|
||||
evaluate_test->expression.c_str(),
|
||||
evaluate_test->evaluable ? "evaluable" : "not evaluable",
|
||||
result ? "evaluted" : "not evaluated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the results.
|
||||
for (map<string, unsigned int>::const_iterator validate_iterator =
|
||||
evaluate_test_set->validate_data->begin();
|
||||
validate_iterator != evaluate_test_set->validate_data->end();
|
||||
++validate_iterator) {
|
||||
const string identifier = validate_iterator->first;
|
||||
unsigned int expected_value = validate_iterator->second;
|
||||
|
||||
map<string, unsigned int>::const_iterator dictionary_iterator =
|
||||
evaluate_test_set->dictionary->find(identifier);
|
||||
|
||||
// The identifier must exist in the dictionary.
|
||||
if (dictionary_iterator == evaluate_test_set->dictionary->end()) {
|
||||
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
|
||||
"validate identifier \"%s\", "
|
||||
"expected %d, observed not found\n",
|
||||
evaluate_test_set_index, evaluate_test_set_count,
|
||||
identifier.c_str(), expected_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The value in the dictionary must be the same as the expected value.
|
||||
unsigned int observed_value = dictionary_iterator->second;
|
||||
if (expected_value != observed_value) {
|
||||
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
|
||||
"validate identifier \"%s\", "
|
||||
"expected %d, observed %d\n",
|
||||
evaluate_test_set_index, evaluate_test_set_count,
|
||||
identifier.c_str(), expected_value, observed_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The value must be set in the "assigned" dictionary if it was a
|
||||
// variable. It must not have been assigned if it was a constant.
|
||||
bool expected_assigned = identifier[0] == '$';
|
||||
bool observed_assigned = false;
|
||||
PostfixEvaluator<unsigned int>::DictionaryValidityType::const_iterator
|
||||
iterator_assigned = assigned.find(identifier);
|
||||
if (iterator_assigned != assigned.end()) {
|
||||
observed_assigned = iterator_assigned->second;
|
||||
}
|
||||
if (expected_assigned != observed_assigned) {
|
||||
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
|
||||
"validate assignment of \"%s\", "
|
||||
"expected %d, observed %d\n",
|
||||
evaluate_test_set_index, evaluate_test_set_count,
|
||||
identifier.c_str(), expected_assigned, observed_assigned);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// 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.
|
||||
|
||||
// process_state.cc: A snapshot of a process, in a fully-digested state.
|
||||
//
|
||||
// See process_state.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#include "google/process_state.h"
|
||||
#include "google/call_stack.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
ProcessState::~ProcessState() {
|
||||
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
|
||||
iterator != threads_.end();
|
||||
++iterator) {
|
||||
delete *iterator;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,153 @@
|
|||
// 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-inl.h: Range map implementation.
|
||||
//
|
||||
// See range_map.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#ifndef PROCESSOR_RANGE_MAP_INL_H__
|
||||
#define PROCESSOR_RANGE_MAP_INL_H__
|
||||
|
||||
|
||||
#include "processor/range_map.h"
|
||||
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool RangeMap<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;
|
||||
|
||||
// Ensure that this range does not overlap with another one already in the
|
||||
// map.
|
||||
MapConstIterator iterator_base = map_.lower_bound(base);
|
||||
MapConstIterator iterator_high = map_.lower_bound(high);
|
||||
|
||||
if (iterator_base != iterator_high) {
|
||||
// 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.
|
||||
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
|
||||
// higher. Regardless, it's an error.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the range in the map by its high address, so that lower_bound can
|
||||
// be used to quickly locate a range by address.
|
||||
map_.insert(MapValue(high, Range(base, entry)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
MapConstIterator iterator = map_.lower_bound(address);
|
||||
if (iterator == map_.end())
|
||||
return false;
|
||||
|
||||
// The map is keyed by the high address of each range, so |address| is
|
||||
// guaranteed to be lower than the range's high address. If |range| is
|
||||
// not directly preceded by another range, it's possible for address to
|
||||
// be below the range's low address, though. When that happens, address
|
||||
// references something not within any range, so return false.
|
||||
if (address < iterator->second.base())
|
||||
return false;
|
||||
|
||||
*entry = iterator->second.entry();
|
||||
if (entry_base)
|
||||
*entry_base = iterator->second.base();
|
||||
if (entry_size)
|
||||
*entry_size = iterator->first - iterator->second.base() + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// If address is within a range, RetrieveRange can handle it.
|
||||
if (RetrieveRange(address, entry, entry_base, entry_size))
|
||||
return true;
|
||||
|
||||
// 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.entry();
|
||||
if (entry_base)
|
||||
*entry_base = iterator->first;
|
||||
if (entry_size)
|
||||
*entry_size = iterator->first - iterator->second.base() + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
void RangeMap<AddressType, EntryType>::Clear() {
|
||||
map_.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_RANGE_MAP_INL_H__
|
|
@ -0,0 +1,113 @@
|
|||
// 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_airbag {
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// 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_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_RANGE_MAP_H__
|
|
@ -0,0 +1,407 @@
|
|||
// 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_unittest.cc: Unit tests for RangeMap
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
|
||||
using google_airbag::linked_ptr;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::RangeMap;
|
||||
|
||||
|
||||
// A CountedObject holds an int. A global (not thread safe!) count of
|
||||
// allocated CountedObjects is maintained to help test memory management.
|
||||
class CountedObject {
|
||||
public:
|
||||
explicit CountedObject(int id) : id_(id) { ++count_; }
|
||||
~CountedObject() { --count_; }
|
||||
|
||||
static int count() { return count_; }
|
||||
int id() const { return id_; }
|
||||
|
||||
private:
|
||||
static int count_;
|
||||
int id_;
|
||||
};
|
||||
|
||||
int CountedObject::count_;
|
||||
|
||||
|
||||
typedef int AddressType;
|
||||
typedef RangeMap< AddressType, linked_ptr<CountedObject> > TestMap;
|
||||
|
||||
|
||||
// RangeTest contains data to use for store and retrieve tests. See
|
||||
// RunTests for descriptions of the tests.
|
||||
struct RangeTest {
|
||||
// Base address to use for test
|
||||
AddressType address;
|
||||
|
||||
// Size of range to use for test
|
||||
AddressType size;
|
||||
|
||||
// Unique ID of range - unstorable ranges must have unique IDs too
|
||||
int id;
|
||||
|
||||
// Whether this range is expected to be stored successfully or not
|
||||
bool expect_storable;
|
||||
};
|
||||
|
||||
|
||||
// A RangeTestSet encompasses multiple RangeTests, which are run in
|
||||
// sequence on the same RangeMap.
|
||||
struct RangeTestSet {
|
||||
// An array of RangeTests
|
||||
const RangeTest *range_tests;
|
||||
|
||||
// The number of tests in the set
|
||||
unsigned int range_test_count;
|
||||
};
|
||||
|
||||
|
||||
// StoreTest uses the data in a RangeTest and calls StoreRange on the
|
||||
// test RangeMap. It returns true if the expected result occurred, and
|
||||
// false if something else happened.
|
||||
bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
|
||||
linked_ptr<CountedObject> object(new CountedObject(range_test->id));
|
||||
bool stored = range_map->StoreRange(range_test->address,
|
||||
range_test->size,
|
||||
object);
|
||||
|
||||
if (stored != range_test->expect_storable) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"StoreRange id %d, expected %s, observed %s\n",
|
||||
range_test->id,
|
||||
range_test->expect_storable ? "storable" : "not storable",
|
||||
stored ? "stored" : "not stored");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// RetrieveTest uses the data in RangeTest and calls RetrieveRange on the
|
||||
// test RangeMap. If it retrieves the expected value (which can be no
|
||||
// map entry at the specified range,) it returns true, otherwise, it returns
|
||||
// false. RetrieveTest will check the values around the base address and
|
||||
// the high address of a range to guard against off-by-one errors.
|
||||
bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
|
||||
for (unsigned int side = 0; side <= 1; ++side) {
|
||||
// When side == 0, check the low side (base address) of each range.
|
||||
// When side == 1, check the high side (base + size) of each range.
|
||||
|
||||
// Check one-less and one-greater than the target address in addition
|
||||
// to the target address itself.
|
||||
|
||||
// If the size of the range is only 1, don't check one greater than
|
||||
// the base or one less than the high - for a successfully stored
|
||||
// range, these tests would erroneously fail because the range is too
|
||||
// small.
|
||||
AddressType low_offset = -1;
|
||||
AddressType high_offset = 1;
|
||||
if (range_test->size == 1) {
|
||||
if (!side) // When checking the low side,
|
||||
high_offset = 0; // don't check one over the target.
|
||||
else // When checking the high side,
|
||||
low_offset = 0; // don't check one under the target.
|
||||
}
|
||||
|
||||
for (AddressType offset = low_offset; offset <= high_offset; ++offset) {
|
||||
AddressType address =
|
||||
offset +
|
||||
(!side ? range_test->address :
|
||||
range_test->address + range_test->size - 1);
|
||||
|
||||
bool expected_result = false; // This is correct for tests not stored.
|
||||
if (range_test->expect_storable) {
|
||||
if (offset == 0) // When checking the target address,
|
||||
expected_result = true; // test should always succeed.
|
||||
else if (offset == -1) // When checking one below the target,
|
||||
expected_result = side; // should fail low and succeed high.
|
||||
else // When checking one above the target,
|
||||
expected_result = !side; // should succeed low and fail high.
|
||||
}
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base;
|
||||
AddressType retrieved_size;
|
||||
bool retrieved = range_map->RetrieveRange(address, &object,
|
||||
&retrieved_base,
|
||||
&retrieved_size);
|
||||
|
||||
bool observed_result = retrieved && object->id() == range_test->id;
|
||||
|
||||
if (observed_result != expected_result) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"RetrieveRange id %d, side %d, offset %d, "
|
||||
"expected %s, observed %s\n",
|
||||
range_test->id,
|
||||
side,
|
||||
offset,
|
||||
expected_result ? "true" : "false",
|
||||
observed_result ? "true" : "false");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a range was successfully retrieved, check that the returned
|
||||
// bounds match the range as stored.
|
||||
if (observed_result == true &&
|
||||
(retrieved_base != range_test->address ||
|
||||
retrieved_size != range_test->size)) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"RetrieveRange id %d, side %d, offset %d, "
|
||||
"expected base/size %d/%d, observed %d/%d\n",
|
||||
range_test->id,
|
||||
side,
|
||||
offset,
|
||||
range_test->address, range_test->size,
|
||||
retrieved_base, retrieved_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, check RetrieveNearestRange. The nearest range is always
|
||||
// expected to be different from the test range when checking one
|
||||
// less than the low side.
|
||||
bool expected_nearest = range_test->expect_storable;
|
||||
if (!side && offset < 0)
|
||||
expected_nearest = false;
|
||||
|
||||
linked_ptr<CountedObject> nearest_object;
|
||||
AddressType nearest_base;
|
||||
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
|
||||
&nearest_object,
|
||||
&nearest_base,
|
||||
NULL);
|
||||
|
||||
// When checking one greater than the high side, RetrieveNearestRange
|
||||
// should usually return the test range. When a different range begins
|
||||
// at that address, though, then RetrieveNearestRange should return the
|
||||
// range at the address instead of the test range.
|
||||
if (side && offset > 0 && nearest_base == address) {
|
||||
expected_nearest = false;
|
||||
}
|
||||
|
||||
bool observed_nearest = retrieved_nearest &&
|
||||
nearest_object->id() == range_test->id;
|
||||
|
||||
if (observed_nearest != expected_nearest) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"RetrieveNearestRange id %d, side %d, offset %d, "
|
||||
"expected %s, observed %s\n",
|
||||
range_test->id,
|
||||
side,
|
||||
offset,
|
||||
expected_nearest ? "true" : "false",
|
||||
observed_nearest ? "true" : "false");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// RunTests runs a series of test sets.
|
||||
bool RunTests() {
|
||||
// These tests will be run sequentially. The first set of tests exercises
|
||||
// most functions of RangeTest, and verifies all of the bounds-checking.
|
||||
const RangeTest range_tests_0[] = {
|
||||
{ INT_MIN, 16, 1, true }, // lowest possible range
|
||||
{ -2, 5, 2, true }, // a range through zero
|
||||
{ INT_MAX - 9, 11, 3, false }, // tests anti-overflow
|
||||
{ INT_MAX - 9, 10, 4, true }, // highest possible range
|
||||
{ 5, 0, 5, false }, // tests anti-zero-size
|
||||
{ 5, 1, 6, true }, // smallest possible range
|
||||
{ -20, 15, 7, true }, // entirely negative
|
||||
|
||||
{ 10, 10, 10, true }, // causes the following tests to fail
|
||||
{ 9, 10, 11, false }, // one-less base, one-less high
|
||||
{ 9, 11, 12, false }, // one-less base, identical high
|
||||
{ 9, 12, 13, false }, // completely contains existing
|
||||
{ 10, 9, 14, false }, // identical base, one-less high
|
||||
{ 10, 10, 15, false }, // exactly identical to existing range
|
||||
{ 10, 11, 16, false }, // identical base, one-greater high
|
||||
{ 11, 8, 17, false }, // contained completely within
|
||||
{ 11, 9, 18, false }, // one-greater base, identical high
|
||||
{ 11, 10, 19, false }, // one-greater base, one-greater high
|
||||
{ 9, 2, 20, false }, // overlaps bottom by one
|
||||
{ 10, 1, 21, false }, // overlaps bottom by one, contained
|
||||
{ 19, 1, 22, false }, // overlaps top by one, contained
|
||||
{ 19, 2, 23, false }, // overlaps top by one
|
||||
|
||||
{ 9, 1, 24, true }, // directly below without overlap
|
||||
{ 20, 1, 25, true }, // directly above without overlap
|
||||
|
||||
{ 6, 3, 26, true }, // exactly between two ranges, gapless
|
||||
{ 7, 3, 27, false }, // tries to span two ranges
|
||||
{ 7, 5, 28, false }, // tries to span three ranges
|
||||
{ 4, 20, 29, false }, // tries to contain several ranges
|
||||
|
||||
{ 30, 50, 30, true },
|
||||
{ 90, 25, 31, true },
|
||||
{ 35, 65, 32, false }, // tries to span two noncontiguous
|
||||
{ 120, 10000, 33, true }, // > 8-bit
|
||||
{ 20000, 20000, 34, true }, // > 8-bit
|
||||
{ 0x10001, 0x10001, 35, true }, // > 16-bit
|
||||
|
||||
{ 27, -1, 36, false } // tests high < base
|
||||
};
|
||||
|
||||
// Attempt to fill the entire space. The entire space must be filled with
|
||||
// three stores because AddressType is signed for these tests, so RangeMap
|
||||
// treats the size as signed and rejects sizes that appear to be negative.
|
||||
// Even if these tests were run as unsigned, two stores would be needed
|
||||
// to fill the space because the entire size of the space could only be
|
||||
// described by using one more bit than would be present in AddressType.
|
||||
const RangeTest range_tests_1[] = {
|
||||
{ INT_MIN, INT_MAX, 50, true }, // From INT_MIN to -2, inclusive
|
||||
{ -1, 2, 51, true }, // From -1 to 0, inclusive
|
||||
{ 1, INT_MAX, 52, true }, // From 1 to INT_MAX, inclusive
|
||||
{ INT_MIN, INT_MAX, 53, false }, // Can't fill the space twice
|
||||
{ -1, 2, 54, false },
|
||||
{ 1, INT_MAX, 55, false },
|
||||
{ -3, 6, 56, false }, // -3 to 2, inclusive - spans 3 ranges
|
||||
};
|
||||
|
||||
// A light round of testing to verify that RetrieveRange does the right
|
||||
// the right thing at the extremities of the range when nothing is stored
|
||||
// there. Checks are forced without storing anything at the extremities
|
||||
// by setting size = 0.
|
||||
const RangeTest range_tests_2[] = {
|
||||
{ INT_MIN, 0, 100, false }, // makes RetrieveRange check low end
|
||||
{ -1, 3, 101, true },
|
||||
{ INT_MAX, 0, 102, false }, // makes RetrieveRange check high end
|
||||
};
|
||||
|
||||
// Similar to the previous test set, but with a couple of ranges closer
|
||||
// to the extremities.
|
||||
const RangeTest range_tests_3[] = {
|
||||
{ INT_MIN + 1, 1, 110, true },
|
||||
{ INT_MAX - 1, 1, 111, true },
|
||||
{ INT_MIN, 0, 112, false }, // makes RetrieveRange check low end
|
||||
{ INT_MAX, 0, 113, false } // makes RetrieveRange check high end
|
||||
};
|
||||
|
||||
// The range map is cleared between sets of tests listed here.
|
||||
const RangeTestSet range_test_sets[] = {
|
||||
{ range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) },
|
||||
{ range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) },
|
||||
{ range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) },
|
||||
{ range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) },
|
||||
{ range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) } // Run again
|
||||
};
|
||||
|
||||
// Maintain the range map in a pointer so that deletion can be meaningfully
|
||||
// tested.
|
||||
scoped_ptr<TestMap> range_map(new TestMap());
|
||||
|
||||
// Run all of the test sets in sequence.
|
||||
unsigned int range_test_set_count = sizeof(range_test_sets) /
|
||||
sizeof(RangeTestSet);
|
||||
for (unsigned int range_test_set_index = 0;
|
||||
range_test_set_index < range_test_set_count;
|
||||
++range_test_set_index) {
|
||||
const RangeTest *range_tests =
|
||||
range_test_sets[range_test_set_index].range_tests;
|
||||
unsigned int range_test_count =
|
||||
range_test_sets[range_test_set_index].range_test_count;
|
||||
|
||||
// Run the StoreRange test, which validates StoreRange and initializes
|
||||
// the RangeMap with data for the RetrieveRange test.
|
||||
int stored_count = 0; // The number of ranges successfully stored
|
||||
for (unsigned int range_test_index = 0;
|
||||
range_test_index < range_test_count;
|
||||
++range_test_index) {
|
||||
const RangeTest *range_test = &range_tests[range_test_index];
|
||||
if (!StoreTest(range_map.get(), range_test))
|
||||
return false;
|
||||
|
||||
if (range_test->expect_storable)
|
||||
++stored_count;
|
||||
}
|
||||
|
||||
// There should be exactly one CountedObject for everything successfully
|
||||
// stored in the RangeMap.
|
||||
if (CountedObject::count() != stored_count) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"stored object counts don't match, expected %d, observed %d\n",
|
||||
stored_count,
|
||||
CountedObject::count());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the RetrieveRange test
|
||||
for (unsigned int range_test_index = 0;
|
||||
range_test_index < range_test_count;
|
||||
++range_test_index) {
|
||||
const RangeTest *range_test = &range_tests[range_test_index];
|
||||
if (!RetrieveTest(range_map.get(), range_test))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear the map between test sets. If this is the final test set,
|
||||
// delete the map instead to test destruction.
|
||||
if (range_test_set_index < range_test_set_count - 1)
|
||||
range_map->Clear();
|
||||
else
|
||||
range_map.reset();
|
||||
|
||||
// Test that all stored objects are freed when the RangeMap is cleared
|
||||
// or deleted.
|
||||
if (CountedObject::count() != 0) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"did not free all objects after %s, %d still allocated\n",
|
||||
range_test_set_index < range_test_set_count - 1 ? "clear"
|
||||
: "delete",
|
||||
CountedObject::count());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
|
||||
// Copyright (c) 2001, 2002 Peter Dimov
|
||||
//
|
||||
// Permission to copy, use, modify, sell and distribute this software
|
||||
// is granted provided this copyright notice appears in all copies.
|
||||
// This software is provided "as is" without express or implied
|
||||
// warranty, and with no claim as to its suitability for any purpose.
|
||||
//
|
||||
// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
|
||||
//
|
||||
|
||||
// scoped_ptr mimics a built-in pointer except that it guarantees deletion
|
||||
// of the object pointed to, either on destruction of the scoped_ptr or via
|
||||
// an explicit reset(). scoped_ptr is a simple solution for simple needs;
|
||||
// use shared_ptr or std::auto_ptr if your needs are more complex.
|
||||
|
||||
// *** NOTE ***
|
||||
// If your scoped_ptr is a class member of class FOO pointing to a
|
||||
// forward declared type BAR (as shown below), then you MUST use a non-inlined
|
||||
// version of the destructor. The destructor of a scoped_ptr (called from
|
||||
// FOO's destructor) must have a complete definition of BAR in order to
|
||||
// destroy it. Example:
|
||||
//
|
||||
// -- foo.h --
|
||||
// class BAR;
|
||||
//
|
||||
// class FOO {
|
||||
// public:
|
||||
// FOO();
|
||||
// ~FOO(); // Required for sources that instantiate class FOO to compile!
|
||||
//
|
||||
// private:
|
||||
// scoped_ptr<BAR> bar_;
|
||||
// };
|
||||
//
|
||||
// -- foo.cc --
|
||||
// #include "foo.h"
|
||||
// FOO::~FOO() {} // Empty, but must be non-inlined to FOO's class definition.
|
||||
|
||||
// scoped_ptr_malloc added by Google
|
||||
// When one of these goes out of scope, instead of doing a delete or
|
||||
// delete[], it calls free(). scoped_ptr_malloc<char> is likely to see
|
||||
// much more use than any other specializations.
|
||||
|
||||
// release() added by Google
|
||||
// Use this to conditionally transfer ownership of a heap-allocated object
|
||||
// to the caller, usually on method success.
|
||||
|
||||
#ifndef PROCESSOR_SCOPED_PTR_H__
|
||||
#define PROCESSOR_SCOPED_PTR_H__
|
||||
|
||||
#include <cstddef> // for std::ptrdiff_t
|
||||
#include <assert.h> // for assert
|
||||
#include <stdlib.h> // for free() decl
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
template <typename T>
|
||||
class scoped_ptr {
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
|
||||
scoped_ptr(scoped_ptr const &);
|
||||
scoped_ptr & operator=(scoped_ptr const &);
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
|
||||
explicit scoped_ptr(T* p = 0): ptr(p) {}
|
||||
|
||||
~scoped_ptr() {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
void reset(T* p = 0) {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
|
||||
if (ptr != p) {
|
||||
delete ptr;
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
assert(ptr != 0);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
assert(ptr != 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool operator==(T* p) const {
|
||||
return ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(T* p) const {
|
||||
return ptr != p;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void swap(scoped_ptr & b) {
|
||||
T* tmp = b.ptr;
|
||||
b.ptr = ptr;
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr;
|
||||
ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// no reason to use these: each scoped_ptr should have its own object
|
||||
template <typename U> bool operator==(scoped_ptr<U> const& p) const;
|
||||
template <typename U> bool operator!=(scoped_ptr<U> const& p) const;
|
||||
};
|
||||
|
||||
template<typename T> inline
|
||||
void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator==(T* p, const scoped_ptr<T>& b) {
|
||||
return p == b.get();
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator!=(T* p, const scoped_ptr<T>& b) {
|
||||
return p != b.get();
|
||||
}
|
||||
|
||||
// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
|
||||
// is guaranteed, either on destruction of the scoped_array or via an explicit
|
||||
// reset(). Use shared_array or std::vector if your needs are more complex.
|
||||
|
||||
template<typename T>
|
||||
class scoped_array {
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
|
||||
scoped_array(scoped_array const &);
|
||||
scoped_array & operator=(scoped_array const &);
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
|
||||
explicit scoped_array(T* p = 0) : ptr(p) {}
|
||||
|
||||
~scoped_array() {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
delete[] ptr;
|
||||
}
|
||||
|
||||
void reset(T* p = 0) {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
|
||||
if (ptr != p) {
|
||||
delete [] ptr;
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator[](std::ptrdiff_t i) const {
|
||||
assert(ptr != 0);
|
||||
assert(i >= 0);
|
||||
return ptr[i];
|
||||
}
|
||||
|
||||
bool operator==(T* p) const {
|
||||
return ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(T* p) const {
|
||||
return ptr != p;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void swap(scoped_array & b) {
|
||||
T* tmp = b.ptr;
|
||||
b.ptr = ptr;
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr;
|
||||
ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// no reason to use these: each scoped_array should have its own object
|
||||
template <typename U> bool operator==(scoped_array<U> const& p) const;
|
||||
template <typename U> bool operator!=(scoped_array<U> const& p) const;
|
||||
};
|
||||
|
||||
template<class T> inline
|
||||
void swap(scoped_array<T>& a, scoped_array<T>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator==(T* p, const scoped_array<T>& b) {
|
||||
return p == b.get();
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator!=(T* p, const scoped_array<T>& b) {
|
||||
return p != b.get();
|
||||
}
|
||||
|
||||
|
||||
// This class wraps the c library function free() in a class that can be
|
||||
// passed as a template argument to scoped_ptr_malloc below.
|
||||
class ScopedPtrMallocFree {
|
||||
public:
|
||||
inline void operator()(void* x) const {
|
||||
free(x);
|
||||
}
|
||||
};
|
||||
|
||||
// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
|
||||
// second template argument, the functor used to free the object.
|
||||
|
||||
template<typename T, typename FreeProc = ScopedPtrMallocFree>
|
||||
class scoped_ptr_malloc {
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
|
||||
scoped_ptr_malloc(scoped_ptr_malloc const &);
|
||||
scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
|
||||
explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
|
||||
|
||||
~scoped_ptr_malloc() {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
free_((void*) ptr);
|
||||
}
|
||||
|
||||
void reset(T* p = 0) {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
|
||||
if (ptr != p) {
|
||||
free_((void*) ptr);
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
assert(ptr != 0);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
assert(ptr != 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool operator==(T* p) const {
|
||||
return ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(T* p) const {
|
||||
return ptr != p;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void swap(scoped_ptr_malloc & b) {
|
||||
T* tmp = b.ptr;
|
||||
b.ptr = ptr;
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr;
|
||||
ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// no reason to use these: each scoped_ptr_malloc should have its own object
|
||||
template <typename U, typename GP>
|
||||
bool operator==(scoped_ptr_malloc<U, GP> const& p) const;
|
||||
template <typename U, typename GP>
|
||||
bool operator!=(scoped_ptr_malloc<U, GP> const& p) const;
|
||||
|
||||
static FreeProc const free_;
|
||||
};
|
||||
|
||||
template<typename T, typename FP>
|
||||
FP const scoped_ptr_malloc<T,FP>::free_ = FP();
|
||||
|
||||
template<typename T, typename FP> inline
|
||||
void swap(scoped_ptr_malloc<T,FP>& a, scoped_ptr_malloc<T,FP>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T, typename FP> inline
|
||||
bool operator==(T* p, const scoped_ptr_malloc<T,FP>& b) {
|
||||
return p == b.get();
|
||||
}
|
||||
|
||||
template<typename T, typename FP> inline
|
||||
bool operator!=(T* p, const scoped_ptr_malloc<T,FP>& b) {
|
||||
return p != b.get();
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // PROCESSOR_SCOPED_PTR_H__
|
|
@ -0,0 +1,549 @@
|
|||
// 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 "processor/source_line_resolver.h"
|
||||
#include "google/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_airbag {
|
||||
|
||||
struct SourceLineResolver::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 SourceLineResolver::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 SourceLineResolver::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 SourceLineResolver::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(MemAddr address, StackFrame *frame) const;
|
||||
|
||||
private:
|
||||
friend class SourceLineResolver;
|
||||
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];
|
||||
};
|
||||
|
||||
SourceLineResolver::SourceLineResolver() : modules_(new ModuleMap) {
|
||||
}
|
||||
|
||||
SourceLineResolver::~SourceLineResolver() {
|
||||
ModuleMap::iterator it;
|
||||
for (it = modules_->begin(); it != modules_->end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
delete modules_;
|
||||
}
|
||||
|
||||
bool SourceLineResolver::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 SourceLineResolver::HasModule(const string &module_name) const {
|
||||
return modules_->find(module_name) != modules_->end();
|
||||
}
|
||||
|
||||
StackFrameInfo* SourceLineResolver::FillSourceLineInfo(
|
||||
StackFrame *frame) const {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module_name);
|
||||
if (it != modules_->end()) {
|
||||
return it->second->LookupAddress(frame->instruction - frame->module_base,
|
||||
frame);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool SourceLineResolver::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];
|
||||
Function *cur_func = NULL;
|
||||
|
||||
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 = ParseFunction(buffer);
|
||||
if (!cur_func) {
|
||||
return false;
|
||||
}
|
||||
if (!functions_.StoreRange(cur_func->address, cur_func->size,
|
||||
linked_ptr<Function>(cur_func))) {
|
||||
return false;
|
||||
}
|
||||
} else if (strncmp(buffer, "PUBLIC ", 7) == 0) {
|
||||
// Clear cur_func: public symbols don't contain line number information.
|
||||
cur_func = NULL;
|
||||
|
||||
if (!ParsePublicSymbol(buffer)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!cur_func) {
|
||||
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* SourceLineResolver::Module::LookupAddress(
|
||||
MemAddr address, StackFrame *frame) const {
|
||||
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 + 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 + 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 + 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 SourceLineResolver::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 *token = strtok(line, " \r\n");
|
||||
while (token && --remaining > 0) {
|
||||
tokens->push_back(token);
|
||||
if (remaining > 1)
|
||||
token = strtok(NULL, " \r\n");
|
||||
}
|
||||
|
||||
// If there's anything left, just add it as a single token.
|
||||
if (!remaining > 0) {
|
||||
if ((token = strtok(NULL, "\r\n"))) {
|
||||
tokens->push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
return tokens->size() == static_cast<unsigned int>(max_tokens);
|
||||
}
|
||||
|
||||
void SourceLineResolver::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)));
|
||||
}
|
||||
}
|
||||
|
||||
SourceLineResolver::Function* SourceLineResolver::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);
|
||||
}
|
||||
|
||||
SourceLineResolver::Line* SourceLineResolver::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 SourceLineResolver::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 SourceLineResolver::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 SourceLineResolver::HashString::operator()(const string &s) const {
|
||||
return hash<const char*>()(s.c_str());
|
||||
}
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,98 @@
|
|||
// 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.
|
||||
|
||||
// SourceLineResolver returns function/file/line info for a memory address.
|
||||
// It uses address map files produced by a compatible writer, e.g.
|
||||
// PDBSourceLineWriter.
|
||||
|
||||
#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_H__
|
||||
#define PROCESSOR_SOURCE_LINE_RESOLVER_H__
|
||||
|
||||
#include <string>
|
||||
#include <ext/hash_map>
|
||||
#include "google/airbag_types.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
using std::string;
|
||||
using __gnu_cxx::hash_map;
|
||||
|
||||
struct StackFrame;
|
||||
struct StackFrameInfo;
|
||||
|
||||
class SourceLineResolver {
|
||||
public:
|
||||
typedef u_int64_t MemAddr;
|
||||
|
||||
SourceLineResolver();
|
||||
~SourceLineResolver();
|
||||
|
||||
// Adds a module to this resolver, returning true on success.
|
||||
//
|
||||
// module_name may be an arbitrary string. Typically, it will be the
|
||||
// filename of the module, optionally with version identifiers.
|
||||
//
|
||||
// map_file should contain line/address mappings for this module.
|
||||
bool LoadModule(const string &module_name, const string &map_file);
|
||||
|
||||
// Returns true if a module with the given name has been loaded.
|
||||
bool HasModule(const string &module_name) const;
|
||||
|
||||
// Fills in the function_base, function_name, source_file_name,
|
||||
// and source_line fields of the StackFrame. The instruction and
|
||||
// module_name fields must already be filled in. Additional debugging
|
||||
// information, if available, is returned. If the information is not
|
||||
// available, returns NULL. A NULL return value does not indicate an
|
||||
// error. The caller takes ownership of any returned StackFrameInfo
|
||||
// object.
|
||||
StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const;
|
||||
|
||||
private:
|
||||
template<class T> class MemAddrMap;
|
||||
struct Line;
|
||||
struct Function;
|
||||
struct PublicSymbol;
|
||||
struct File;
|
||||
struct HashString {
|
||||
size_t operator()(const string &s) const;
|
||||
};
|
||||
class Module;
|
||||
|
||||
// All of the modules we've loaded
|
||||
typedef hash_map<string, Module*, HashString> ModuleMap;
|
||||
ModuleMap *modules_;
|
||||
|
||||
// Disallow unwanted copy ctor and assignment operator
|
||||
SourceLineResolver(const SourceLineResolver&);
|
||||
void operator=(const SourceLineResolver&);
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
#endif // PROCESSOR_SOURCE_LINE_RESOLVER_H__
|
|
@ -0,0 +1,154 @@
|
|||
// 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>
|
||||
#include "processor/source_line_resolver.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
|
||||
using std::string;
|
||||
using google_airbag::linked_ptr;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::SourceLineResolver;
|
||||
using google_airbag::StackFrame;
|
||||
using google_airbag::StackFrameInfo;
|
||||
|
||||
#define ASSERT_TRUE(cond) \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define ASSERT_FALSE(cond) ASSERT_TRUE(!(cond))
|
||||
|
||||
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
|
||||
|
||||
static bool VerifyEmpty(const StackFrame &frame) {
|
||||
ASSERT_TRUE(frame.function_name.empty());
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
ASSERT_EQ(frame.source_line, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ClearSourceLineInfo(StackFrame *frame) {
|
||||
frame->function_name.clear();
|
||||
frame->source_file_name.clear();
|
||||
frame->source_line = 0;
|
||||
}
|
||||
|
||||
static bool RunTests() {
|
||||
string testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata";
|
||||
|
||||
SourceLineResolver resolver;
|
||||
ASSERT_TRUE(resolver.LoadModule("module1", testdata_dir + "/module1.out"));
|
||||
ASSERT_TRUE(resolver.HasModule("module1"));
|
||||
ASSERT_TRUE(resolver.LoadModule("module2", testdata_dir + "/module2.out"));
|
||||
ASSERT_TRUE(resolver.HasModule("module2"));
|
||||
|
||||
StackFrame frame;
|
||||
frame.instruction = 0x1000;
|
||||
frame.module_name = "module1";
|
||||
scoped_ptr<StackFrameInfo> frame_info(resolver.FillSourceLineInfo(&frame));
|
||||
ASSERT_EQ(frame.function_name, "Function1_1");
|
||||
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
|
||||
ASSERT_EQ(frame.source_line, 44);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_FALSE(frame_info->allocates_base_pointer);
|
||||
ASSERT_EQ(frame_info->program_string,
|
||||
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
|
||||
|
||||
ClearSourceLineInfo(&frame);
|
||||
frame.instruction = 0x800;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
ASSERT_TRUE(VerifyEmpty(frame));
|
||||
ASSERT_FALSE(frame_info.get());
|
||||
|
||||
frame.instruction = 0x1280;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
ASSERT_EQ(frame.function_name, "Function1_3");
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
ASSERT_EQ(frame.source_line, 0);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_FALSE(frame_info->allocates_base_pointer);
|
||||
ASSERT_TRUE(frame_info->program_string.empty());
|
||||
|
||||
frame.instruction = 0x1380;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
ASSERT_EQ(frame.function_name, "Function1_4");
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
ASSERT_EQ(frame.source_line, 0);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_FALSE(frame_info->allocates_base_pointer);
|
||||
ASSERT_FALSE(frame_info->program_string.empty());
|
||||
|
||||
frame.instruction = 0x2180;
|
||||
frame.module_name = "module2";
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
ASSERT_EQ(frame.function_name, "Function2_2");
|
||||
ASSERT_EQ(frame.source_file_name, "file2_2.cc");
|
||||
ASSERT_EQ(frame.source_line, 21);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_EQ(frame_info->prolog_size, 1);
|
||||
|
||||
frame.instruction = 0x216f;
|
||||
frame.module_name = "module2";
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Public2_1");
|
||||
|
||||
ClearSourceLineInfo(&frame);
|
||||
frame.instruction = 0x219f;
|
||||
frame.module_name = "module2";
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_TRUE(frame.function_name.empty());
|
||||
|
||||
frame.instruction = 0x21a0;
|
||||
frame.module_name = "module2";
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Public2_2");
|
||||
|
||||
ASSERT_FALSE(resolver.LoadModule("module3",
|
||||
testdata_dir + "/module3_bad.out"));
|
||||
ASSERT_FALSE(resolver.HasModule("module3"));
|
||||
ASSERT_FALSE(resolver.LoadModule("module4",
|
||||
testdata_dir + "/invalid-filename"));
|
||||
ASSERT_FALSE(resolver.HasModule("module4"));
|
||||
ASSERT_FALSE(resolver.HasModule("invalid-module"));
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (!RunTests()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
// 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.
|
||||
|
||||
// stack_frame_info.h: Holds debugging information about a stack frame.
|
||||
//
|
||||
// This structure is specific to Windows debugging information obtained
|
||||
// from pdb files using the DIA API.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACK_FRAME_INFO_H__
|
||||
#define PROCESSOR_STACK_FRAME_INFO_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
struct StackFrameInfo {
|
||||
public:
|
||||
enum Validity {
|
||||
VALID_NONE = 0,
|
||||
VALID_PARAMETER_SIZE = 1,
|
||||
VALID_ALL = -1
|
||||
};
|
||||
|
||||
StackFrameInfo() : valid(VALID_NONE),
|
||||
prolog_size(0),
|
||||
epilog_size(0),
|
||||
parameter_size(0),
|
||||
saved_register_size(0),
|
||||
local_size(0),
|
||||
max_stack_size(0),
|
||||
allocates_base_pointer(0),
|
||||
program_string() {}
|
||||
|
||||
StackFrameInfo(u_int32_t set_prolog_size,
|
||||
u_int32_t set_epilog_size,
|
||||
u_int32_t set_parameter_size,
|
||||
u_int32_t set_saved_register_size,
|
||||
u_int32_t set_local_size,
|
||||
u_int32_t set_max_stack_size,
|
||||
int set_allocates_base_pointer,
|
||||
const std::string set_program_string)
|
||||
: valid(VALID_ALL),
|
||||
prolog_size(set_prolog_size),
|
||||
epilog_size(set_epilog_size),
|
||||
parameter_size(set_parameter_size),
|
||||
saved_register_size(set_saved_register_size),
|
||||
local_size(set_local_size),
|
||||
max_stack_size(set_max_stack_size),
|
||||
allocates_base_pointer(set_allocates_base_pointer),
|
||||
program_string(set_program_string) {}
|
||||
|
||||
// CopyFrom makes "this" StackFrameInfo object identical to "that".
|
||||
void CopyFrom(const StackFrameInfo &that) {
|
||||
valid = that.valid;
|
||||
prolog_size = that.prolog_size;
|
||||
epilog_size = that.epilog_size;
|
||||
parameter_size = that.parameter_size;
|
||||
saved_register_size = that.saved_register_size;
|
||||
local_size = that.local_size;
|
||||
max_stack_size = that.max_stack_size;
|
||||
allocates_base_pointer = that.allocates_base_pointer;
|
||||
program_string = that.program_string;
|
||||
}
|
||||
|
||||
// Clears the StackFrameInfo object so that users will see it as though
|
||||
// it contains no information.
|
||||
void Clear() {
|
||||
valid = VALID_NONE;
|
||||
program_string.erase();
|
||||
}
|
||||
|
||||
// Identifies which fields in the structure are valid. This is of
|
||||
// type Validity, but it is defined as an int because it's not
|
||||
// possible to OR values into an enumerated type. Users must check
|
||||
// this field before using any other.
|
||||
int valid;
|
||||
|
||||
// These values come from IDiaFrameData.
|
||||
u_int32_t prolog_size;
|
||||
u_int32_t epilog_size;
|
||||
u_int32_t parameter_size;
|
||||
u_int32_t saved_register_size;
|
||||
u_int32_t local_size;
|
||||
u_int32_t max_stack_size;
|
||||
|
||||
// Only one of allocates_base_pointer or program_string will be valid.
|
||||
// If program_string is empty, use allocates_base_pointer.
|
||||
bool allocates_base_pointer;
|
||||
std::string program_string;
|
||||
};
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_STACK_FRAME_INFO_H__
|
|
@ -0,0 +1,138 @@
|
|||
// 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 "processor/stackwalker.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google/symbol_supplier.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/source_line_resolver.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
||||
Stackwalker::Stackwalker(MemoryRegion *memory, MinidumpModuleList *modules,
|
||||
SymbolSupplier *supplier)
|
||||
: memory_(memory), modules_(modules), supplier_(supplier) {
|
||||
}
|
||||
|
||||
|
||||
CallStack* Stackwalker::Walk() {
|
||||
SourceLineResolver resolver;
|
||||
|
||||
scoped_ptr<CallStack> stack(new CallStack());
|
||||
|
||||
// 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_) {
|
||||
MinidumpModule *module =
|
||||
modules_->GetModuleForAddress(frame->instruction);
|
||||
if (module) {
|
||||
frame->module_name = *(module->GetName());
|
||||
frame->module_base = module->base_address();
|
||||
if (!resolver.HasModule(frame->module_name) && supplier_) {
|
||||
string symbol_file = supplier_->GetSymbolFile(module);
|
||||
if (!symbol_file.empty()) {
|
||||
resolver.LoadModule(frame->module_name, symbol_file);
|
||||
}
|
||||
}
|
||||
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.get(), stack_frame_info));
|
||||
}
|
||||
|
||||
return stack.release();
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
Stackwalker* Stackwalker::StackwalkerForCPU(MinidumpContext *context,
|
||||
MemoryRegion *memory,
|
||||
MinidumpModuleList *modules,
|
||||
SymbolSupplier *supplier) {
|
||||
Stackwalker *cpu_stackwalker = NULL;
|
||||
|
||||
u_int32_t cpu = context->GetContextCPU();
|
||||
switch (cpu) {
|
||||
case MD_CONTEXT_X86:
|
||||
cpu_stackwalker = new StackwalkerX86(context->GetContextX86(),
|
||||
memory, modules, supplier);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_PPC:
|
||||
cpu_stackwalker = new StackwalkerPPC(context->GetContextPPC(),
|
||||
memory, modules, supplier);
|
||||
break;
|
||||
}
|
||||
|
||||
return cpu_stackwalker;
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_airbag
|
|
@ -0,0 +1,136 @@
|
|||
// 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/call_stack.h"
|
||||
#include "google/stack_frame_cpu.h"
|
||||
#include "processor/minidump.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
||||
StackwalkerPPC::StackwalkerPPC(const MDRawContextPPC *context,
|
||||
MemoryRegion *memory,
|
||||
MinidumpModuleList *modules,
|
||||
SymbolSupplier *supplier)
|
||||
: Stackwalker(memory, modules, supplier),
|
||||
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_airbag
|
|
@ -0,0 +1,81 @@
|
|||
// 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.h: ppc-specific stackwalker.
|
||||
//
|
||||
// Provides stack frames given ppc register context and a memory region
|
||||
// corresponding to a ppc stack.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACKWALKER_PPC_H__
|
||||
#define PROCESSOR_STACKWALKER_PPC_H__
|
||||
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "processor/stackwalker.h"
|
||||
#include "processor/minidump_format.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
class MinidumpContext;
|
||||
class MinidumpModuleList;
|
||||
|
||||
|
||||
class StackwalkerPPC : public Stackwalker {
|
||||
public:
|
||||
// context is a MinidumpContext object that gives access to ppc-specific
|
||||
// register state corresponding to the innermost called frame to be
|
||||
// included in the stack. The other arguments are passed directly through
|
||||
// to the base Stackwalker constructor.
|
||||
StackwalkerPPC(const MDRawContextPPC *context,
|
||||
MemoryRegion *memory,
|
||||
MinidumpModuleList *modules,
|
||||
SymbolSupplier *supplier);
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker, using ppc context (stack pointer in %r1,
|
||||
// saved program counter in %srr0) and stack conventions (saved stack
|
||||
// pointer at 0(%r1), return address at 8(0(%r1)).
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
|
||||
|
||||
// Stores the CPU context corresponding to the innermost stack frame to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextPPC *context_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_STACKWALKER_PPC_H__
|
|
@ -0,0 +1,290 @@
|
|||
// 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.
|
||||
|
||||
// stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the
|
||||
// running process' stack as test data, if running on an x86 or ppc and
|
||||
// compiled with gcc. This test is not enabled in the "make check" suite
|
||||
// by default, because certain optimizations interfere with its proper
|
||||
// operation. To turn it on, configure with --enable-selftest.
|
||||
//
|
||||
// Optimizations that cause problems:
|
||||
// - stack frame reuse. The Recursor function here calls itself with
|
||||
// |return Recursor|. When the caller's frame is reused, it will cause
|
||||
// CountCallerFrames to correctly return the same number of frames
|
||||
// in both the caller and callee. This is considered an unexpected
|
||||
// condition in the test, which expects a callee to have one more
|
||||
// caller frame in the stack than its caller.
|
||||
// - frame pointer omission. Even with a stackwalker that understands
|
||||
// this optimization, the code to harness debug information currently
|
||||
// only exists to retrieve it from minidumps, not the current process.
|
||||
//
|
||||
// This test can also serve as a developmental and debugging aid if
|
||||
// PRINT_STACKS is defined.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
#if defined(__GNUC__) && (defined(__i386__) || defined(__ppc__))
|
||||
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "google/call_stack.h"
|
||||
#include "google/stack_frame.h"
|
||||
#include "google/stack_frame_cpu.h"
|
||||
#include "processor/memory_region.h"
|
||||
#include "processor/minidump_format.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
using google_airbag::CallStack;
|
||||
using google_airbag::MemoryRegion;
|
||||
using google_airbag::scoped_ptr;
|
||||
using google_airbag::StackFrame;
|
||||
using google_airbag::StackFramePPC;
|
||||
using google_airbag::StackFrameX86;
|
||||
|
||||
#if defined(__i386__)
|
||||
#include "processor/stackwalker_x86.h"
|
||||
using google_airbag::StackwalkerX86;
|
||||
#elif defined(__ppc__)
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
using google_airbag::StackwalkerPPC;
|
||||
#endif // __i386__ || __ppc__
|
||||
|
||||
#define RECURSION_DEPTH 100
|
||||
|
||||
|
||||
// A simple MemoryRegion subclass that provides direct access to this
|
||||
// process' memory space by pointer.
|
||||
class SelfMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
virtual u_int64_t GetBase() { return 0; }
|
||||
virtual u_int32_t GetSize() { return 0xffffffff; }
|
||||
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) {
|
||||
return GetMemoryAtAddressInternal(address, value); }
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) {
|
||||
return GetMemoryAtAddressInternal(address, value); }
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) {
|
||||
return GetMemoryAtAddressInternal(address, value); }
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) {
|
||||
return GetMemoryAtAddressInternal(address, value); }
|
||||
|
||||
private:
|
||||
template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
|
||||
T* value) {
|
||||
// Without knowing what addresses are actually mapped, just assume that
|
||||
// everything low is not mapped. This helps the stackwalker catch the
|
||||
// end of a stack when it tries to dereference a null or low pointer
|
||||
// in an attempt to find the caller frame. Other unmapped accesses will
|
||||
// cause the program to crash, but that would properly be a test failure.
|
||||
if (address < 0x100)
|
||||
return false;
|
||||
|
||||
u_int8_t* memory = 0;
|
||||
*value = *reinterpret_cast<const T*>(&memory[address]);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
// GetEBP returns the current value of the %ebp register. Because it's
|
||||
// implemented as a function, %ebp itself contains GetEBP's frame pointer
|
||||
// and not the caller's frame pointer. Dereference %ebp to obtain the
|
||||
// caller's frame pointer, which the compiler-generated preamble stored
|
||||
// on the stack (provided frame pointers are not being omitted.) Because
|
||||
// this function depends on the compiler-generated preamble, inlining is
|
||||
// disabled.
|
||||
static u_int32_t GetEBP() __attribute__((noinline));
|
||||
static u_int32_t GetEBP() {
|
||||
u_int32_t ebp;
|
||||
__asm__ __volatile__(
|
||||
"movl (%%ebp), %0"
|
||||
: "=a" (ebp)
|
||||
);
|
||||
return ebp;
|
||||
}
|
||||
|
||||
|
||||
// The caller's %esp is 8 higher than the value of %ebp in this function,
|
||||
// assuming that it's not inlined and that the standard prolog is used.
|
||||
// The CALL instruction places a 4-byte return address on the stack above
|
||||
// the caller's %esp, and this function's prolog will save the caller's %ebp
|
||||
// on the stack as well, for another 4 bytes, before storing %esp in %ebp.
|
||||
static u_int32_t GetESP() __attribute__((noinline));
|
||||
static u_int32_t GetESP() {
|
||||
u_int32_t ebp;
|
||||
__asm__ __volatile__(
|
||||
"movl %%ebp, %0"
|
||||
: "=a" (ebp)
|
||||
);
|
||||
return ebp + 8;
|
||||
}
|
||||
|
||||
|
||||
// GetEIP returns the instruction pointer identifying the next instruction
|
||||
// to execute after GetEIP returns. It obtains this information from the
|
||||
// stack, where it was placed by the call instruction that called GetEIP.
|
||||
// This function depends on frame pointers not being omitted. It is possible
|
||||
// to write a pure asm version of this routine that has no compiler-generated
|
||||
// preamble and uses %esp instead of %ebp; that would function in the
|
||||
// absence of frame pointers. However, the simpler approach is used here
|
||||
// because GetEBP and stackwalking necessarily depends on access to frame
|
||||
// pointers. Because this function depends on a call instruction and the
|
||||
// compiler-generated preamble, inlining is disabled.
|
||||
static u_int32_t GetEIP() __attribute__((noinline));
|
||||
static u_int32_t GetEIP() {
|
||||
u_int32_t eip;
|
||||
__asm__ __volatile__(
|
||||
"movl 4(%%ebp), %0"
|
||||
: "=a" (eip)
|
||||
);
|
||||
return eip;
|
||||
}
|
||||
|
||||
|
||||
#elif defined(__ppc__)
|
||||
|
||||
|
||||
// GetSP returns the current value of the %r1 register, which by convention,
|
||||
// is the stack pointer on ppc. Because it's implemented as a function,
|
||||
// %r1 itself contains GetSP's own stack pointer and not the caller's stack
|
||||
// pointer. Dereference %r1 to obtain the caller's stack pointer, which the
|
||||
// compiler-generated prolog stored on the stack. Because this function
|
||||
// depends on the compiler-generated prolog, inlining is disabled.
|
||||
static u_int32_t GetSP() __attribute__((noinline));
|
||||
static u_int32_t GetSP() {
|
||||
u_int32_t sp;
|
||||
__asm__ __volatile__(
|
||||
"lwz %0, 0(r1)"
|
||||
: "=r" (sp)
|
||||
);
|
||||
return sp;
|
||||
}
|
||||
|
||||
|
||||
// GetPC returns the program counter identifying the next instruction to
|
||||
// execute after GetPC returns. It obtains this information from the
|
||||
// link register, where it was placed by the branch instruction that called
|
||||
// GetPC. Because this function depends on the caller's use of a branch
|
||||
// instruction, inlining is disabled.
|
||||
static u_int32_t GetPC() __attribute__((noinline));
|
||||
static u_int32_t GetPC() {
|
||||
u_int32_t lr;
|
||||
__asm__ __volatile__(
|
||||
"mflr %0"
|
||||
: "=r" (lr)
|
||||
);
|
||||
return lr;
|
||||
}
|
||||
|
||||
|
||||
#endif // __i386__ || __ppc__
|
||||
|
||||
|
||||
// CountCallerFrames returns the number of stack frames beneath the function
|
||||
// that called CountCallerFrames. Because this function's return value
|
||||
// is dependent on the size of the stack beneath it, inlining is disabled,
|
||||
// and any function that calls this should not be inlined either.
|
||||
static unsigned int CountCallerFrames() __attribute__((noinline));
|
||||
static unsigned int CountCallerFrames() {
|
||||
SelfMemoryRegion memory;
|
||||
|
||||
#if defined(__i386__)
|
||||
MDRawContextX86 context = MDRawContextX86();
|
||||
context.eip = GetEIP();
|
||||
context.ebp = GetEBP();
|
||||
context.esp = GetESP();
|
||||
|
||||
StackwalkerX86 stackwalker = StackwalkerX86(&context, &memory, NULL, NULL);
|
||||
#elif defined(__ppc__)
|
||||
MDRawContextPPC context = MDRawContextPPC();
|
||||
context.srr0 = GetPC();
|
||||
context.gpr[1] = GetSP();
|
||||
|
||||
StackwalkerPPC stackwalker = StackwalkerPPC(&context, &memory, NULL, NULL);
|
||||
#endif // __i386__ || __ppc__
|
||||
|
||||
scoped_ptr<CallStack> stack(stackwalker.Walk());
|
||||
|
||||
#ifdef PRINT_STACKS
|
||||
printf("\n");
|
||||
for (unsigned int frame_index = 0;
|
||||
frame_index < stack->frames()->size();
|
||||
++frame_index) {
|
||||
StackFrame *frame = stack->frames()->at(frame_index);
|
||||
printf("frame %-3d instruction = 0x%08llx",
|
||||
frame_index, frame->instruction);
|
||||
#if defined(__i386__)
|
||||
StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame);
|
||||
printf(" esp = 0x%08x ebp = 0x%08x\n",
|
||||
frame_x86->context.esp, frame_x86->context.ebp);
|
||||
#elif defined(__ppc__)
|
||||
StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame);
|
||||
printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]);
|
||||
#endif // __i386__ || __ppc__
|
||||
}
|
||||
#endif // PRINT_STACKS
|
||||
|
||||
// Subtract 1 because the caller wants the number of frames beneath
|
||||
// itself. Because the caller called us, subract two for our frame and its
|
||||
// frame, which are included in stack->size().
|
||||
return stack->frames()->size() - 2;
|
||||
}
|
||||
|
||||
|
||||
// Recursor verifies that the number stack frames beneath itself is one more
|
||||
// than the number of stack frames beneath its parent. When depth frames
|
||||
// have been reached, Recursor stops checking and returns success. If the
|
||||
// frame count check fails at any depth, Recursor will stop and return false.
|
||||
// Because this calls CountCallerFrames, inlining is disabled.
|
||||
static bool Recursor(unsigned int depth, unsigned int parent_callers)
|
||||
__attribute__((noinline));
|
||||
static bool Recursor(unsigned int depth, unsigned int parent_callers) {
|
||||
unsigned int callers = CountCallerFrames();
|
||||
if (callers != parent_callers + 1)
|
||||
return false;
|
||||
|
||||
if (depth)
|
||||
return Recursor(depth - 1, callers);
|
||||
|
||||
// depth == 0
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Because this calls CountCallerFrames, inlining is disabled - but because
|
||||
// it's main (and nobody calls it other than the entry point), it wouldn't
|
||||
// be inlined anyway.
|
||||
int main(int argc, char** argv) __attribute__((noinline));
|
||||
int main(int argc, char** argv) {
|
||||
return Recursor(RECURSION_DEPTH, CountCallerFrames()) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
#else // __GNUC__ && (__i386__ || __ppc__)
|
||||
// Not gcc? We use gcc's __asm__.
|
||||
// Not i386 or ppc? We can only test stacks we know how to walk.
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// "make check" interprets an exit status of 77 to mean that the test is
|
||||
// not supported.
|
||||
return 77;
|
||||
}
|
||||
|
||||
|
||||
#endif // __GNUC__ && (__i386__ || __ppc__)
|
|
@ -0,0 +1,313 @@
|
|||
// 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/call_stack.h"
|
||||
#include "google/stack_frame_cpu.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/minidump.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
|
||||
StackwalkerX86::StackwalkerX86(const MDRawContextX86 *context,
|
||||
MemoryRegion *memory,
|
||||
MinidumpModuleList *modules,
|
||||
SymbolSupplier *supplier)
|
||||
: Stackwalker(memory, modules, supplier),
|
||||
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 an Airbag 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.
|
||||
string program_string;
|
||||
if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) {
|
||||
// FPO data available.
|
||||
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.
|
||||
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.
|
||||
//
|
||||
// %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.
|
||||
//
|
||||
// %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 =";
|
||||
}
|
||||
} 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.
|
||||
//
|
||||
// %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;
|
||||
}
|
||||
|
||||
// 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_airbag
|
|
@ -0,0 +1,81 @@
|
|||
// 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.h: x86-specific stackwalker.
|
||||
//
|
||||
// Provides stack frames given x86 register context and a memory region
|
||||
// corresponding to an x86 stack.
|
||||
//
|
||||
// Author: Mark Mentovai
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACKWALKER_X86_H__
|
||||
#define PROCESSOR_STACKWALKER_X86_H__
|
||||
|
||||
|
||||
#include "google/airbag_types.h"
|
||||
#include "processor/stackwalker.h"
|
||||
#include "processor/minidump_format.h"
|
||||
|
||||
namespace google_airbag {
|
||||
|
||||
class MinidumpContext;
|
||||
class MinidumpModuleList;
|
||||
|
||||
|
||||
class StackwalkerX86 : public Stackwalker {
|
||||
public:
|
||||
// context is a MinidumpContext object that gives access to x86-specific
|
||||
// register state corresponding to the innermost called frame to be
|
||||
// included in the stack. The other arguments are passed directly through
|
||||
// to the base Stackwalker constructor.
|
||||
StackwalkerX86(const MDRawContextX86 *context,
|
||||
MemoryRegion *memory,
|
||||
MinidumpModuleList *modules,
|
||||
SymbolSupplier *supplier);
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and
|
||||
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
|
||||
// alternate conventions as guided by stack_frame_info_).
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
|
||||
|
||||
// Stores the CPU context corresponding to the innermost stack frame to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextX86 *context_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_airbag
|
||||
|
||||
|
||||
#endif // PROCESSOR_STACKWALKER_X86_H__
|
Двоичные данные
toolkit/airbag/airbag/src/processor/testdata/minidump2.dmp
поставляемый
Executable file
Двоичные данные
toolkit/airbag/airbag/src/processor/testdata/minidump2.dmp
поставляемый
Executable file
Двоичный файл не отображается.
|
@ -0,0 +1,16 @@
|
|||
FILE 1 file1_1.cc
|
||||
FILE 2 file1_2.cc
|
||||
FILE 3 file1_3.cc
|
||||
FUNC 1000 c 0 Function1_1
|
||||
1000 4 44 1
|
||||
1004 4 45 1
|
||||
1008 4 46 1
|
||||
FUNC 1100 8 4 Function1_2
|
||||
1100 4 65 2
|
||||
1104 4 66 2
|
||||
FUNC 1200 100 8 Function1_3
|
||||
FUNC 1300 100 c Function1_4
|
||||
STACK WIN 4 1000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
|
||||
STACK WIN 4 1100 8 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
|
||||
STACK WIN 4 1100 100 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
|
||||
STACK WIN 4 1300 100 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
|
|
@ -0,0 +1,16 @@
|
|||
FILE 1 file2_1.cc
|
||||
FILE 2 file2_2.cc
|
||||
FILE 3 file2_3.cc
|
||||
FUNC 2000 c 4 Function2_1
|
||||
1000 4 54 1
|
||||
1004 4 55 1
|
||||
1008 4 56 1
|
||||
PUBLIC 2160 0 Public2_1
|
||||
FUNC 2170 14 4 Function2_2
|
||||
2170 6 10 2
|
||||
2176 4 12 2
|
||||
217a 6 13 2
|
||||
2180 4 21 2
|
||||
PUBLIC 21a0 0 Public2_2
|
||||
STACK WIN 4 2000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
|
||||
STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
|
|
@ -0,0 +1,2 @@
|
|||
FILE 1 file1.cc
|
||||
FUNC 1000
|
|
@ -0,0 +1,74 @@
|
|||
// 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.
|
||||
|
||||
// This file is used to generate minidump2.dmp and minidump2.sym.
|
||||
// cl /Zi /Fetest_app.exe test_app.cc dbghelp.lib
|
||||
// Then run test_app to generate a dump, and dump_syms to create the .sym file.
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
static LONG HandleException(EXCEPTION_POINTERS *exinfo) {
|
||||
HANDLE dump_file = CreateFile("dump.dmp",
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
||||
except_info.ThreadId = GetCurrentThreadId();
|
||||
except_info.ExceptionPointers = exinfo;
|
||||
except_info.ClientPointers = false;
|
||||
|
||||
MiniDumpWriteDump(GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
dump_file,
|
||||
MiniDumpNormal,
|
||||
&except_info,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
void CrashFunction() {
|
||||
int *i = NULL;
|
||||
*i = 5; // crash!
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
__try {
|
||||
CrashFunction();
|
||||
} __except(HandleException(GetExceptionInformation())) {
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// 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.
|
||||
|
||||
// Windows utility to dump the line number data from a pdb file to
|
||||
// a text-based format that we can use from the minidump processor.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
|
||||
using std::wstring;
|
||||
using google_airbag::PDBSourceLineWriter;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s <pdb file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wchar_t filename[_MAX_PATH];
|
||||
if (mbstowcs_s(NULL, filename, argv[1], _MAX_PATH) == -1) {
|
||||
fprintf(stderr, "invalid multibyte character in %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
PDBSourceLineWriter writer;
|
||||
if (!writer.Open(wstring(filename), PDBSourceLineWriter::PDB_FILE)) {
|
||||
fprintf(stderr, "Open failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!writer.WriteMap(stdout)) {
|
||||
fprintf(stderr, "WriteMap failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
writer.Close();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="dump_syms"
|
||||
ProjectGUID="{792E1530-E2C5-4289-992E-317BA30E9D9F}"
|
||||
RootNamespace="dumpsyms"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=""$(VSInstallDir)\DIA SDK\include";..\..\.."
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=""$(VSInstallDir)\DIA SDK\lib\diaguids.lib""
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""$(VSInstallDir)\DIA SDK\include";..\..\.."
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies=""$(VSInstallDir)\DIA SDK\lib\diaguids.lib""
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\guid_string.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\pdb_source_line_writer.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\dump_syms.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\guid_string.cc"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\windows\pdb_source_line_writer.cc"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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.
|
||||
|
||||
Release/dump_syms.exe testdata/dump_syms_regtest.pdb > testdata/dump_syms_regtest.new
|
||||
if diff -u testdata/dump_syms_regtest.new testdata/dump_syms_regtest.out >& testdata/dump_syms_regtest.diff; then
|
||||
rm testdata/dump_syms_regtest.diff testdata/dump_syms_regtest.new
|
||||
echo "PASS"
|
||||
else
|
||||
echo "FAIL, see testdata/dump_syms_regtest.[new|diff]"
|
||||
fi
|
1595
toolkit/airbag/airbag/src/tools/windows/dump_syms/testdata/dump_syms_regtest.out
поставляемый
Executable file
1595
toolkit/airbag/airbag/src/tools/windows/dump_syms/testdata/dump_syms_regtest.out
поставляемый
Executable file
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Двоичные данные
toolkit/airbag/airbag/src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb
поставляемый
Executable file
Двоичные данные
toolkit/airbag/airbag/src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb
поставляемый
Executable file
Двоичный файл не отображается.
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче