зеркало из 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]
|
; [Additional Browsing Enhancements]
|
||||||
[abe]
|
[abe]
|
||||||
|
|
||||||
|
; [airbag]
|
||||||
|
bin\crashreporter.exe
|
||||||
|
bin\crashreporter.ini
|
||||||
|
|
||||||
[talkback]
|
[talkback]
|
||||||
bin\extensions\talkback@mozilla.org\install.rdf
|
bin\extensions\talkback@mozilla.org\install.rdf
|
||||||
bin\extensions\talkback@mozilla.org\chrome.manifest
|
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_XPCOM_OBSOLETE = @MOZ_NO_XPCOM_OBSOLETE@
|
||||||
MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
|
MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
|
||||||
NS_PRINTING = @NS_PRINTING@
|
NS_PRINTING = @NS_PRINTING@
|
||||||
|
MOZ_AIRBAG = @MOZ_AIRBAG@
|
||||||
|
|
||||||
MOZ_JAVAXPCOM = @MOZ_JAVAXPCOM@
|
MOZ_JAVAXPCOM = @MOZ_JAVAXPCOM@
|
||||||
JAVA_INCLUDE_PATH="@JAVA_INCLUDE_PATH@"
|
JAVA_INCLUDE_PATH="@JAVA_INCLUDE_PATH@"
|
||||||
|
|
|
@ -256,7 +256,7 @@ _OBJS = \
|
||||||
$(JRI_STUB_CFILES) \
|
$(JRI_STUB_CFILES) \
|
||||||
$(addsuffix .$(OBJ_SUFFIX), $(JMC_GEN)) \
|
$(addsuffix .$(OBJ_SUFFIX), $(JMC_GEN)) \
|
||||||
$(CSRCS:.c=.$(OBJ_SUFFIX)) \
|
$(CSRCS:.c=.$(OBJ_SUFFIX)) \
|
||||||
$(CPPSRCS:.cpp=.$(OBJ_SUFFIX)) \
|
$(patsubst %.cc,%.$(OBJ_SUFFIX),$(CPPSRCS:.cpp=.$(OBJ_SUFFIX))) \
|
||||||
$(CMSRCS:.m=.$(OBJ_SUFFIX)) \
|
$(CMSRCS:.m=.$(OBJ_SUFFIX)) \
|
||||||
$(CMMSRCS:.mm=.$(OBJ_SUFFIX)) \
|
$(CMMSRCS:.mm=.$(OBJ_SUFFIX)) \
|
||||||
$(ASFILES:.$(ASM_SUFFIX)=.$(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
|
||||||
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 ========================================================
|
||||||
dnl = Enable compilation of specific extension modules
|
dnl = Enable compilation of specific extension modules
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
|
@ -7580,6 +7597,7 @@ AC_SUBST(MOZ_SINGLE_PROFILE)
|
||||||
AC_SUBST(MOZ_SPELLCHECK)
|
AC_SUBST(MOZ_SPELLCHECK)
|
||||||
AC_SUBST(MOZ_XPFE_COMPONENTS)
|
AC_SUBST(MOZ_XPFE_COMPONENTS)
|
||||||
AC_SUBST(MOZ_USER_DIR)
|
AC_SUBST(MOZ_USER_DIR)
|
||||||
|
AC_SUBST(MOZ_AIRBAG)
|
||||||
|
|
||||||
AC_SUBST(ENABLE_STRIP)
|
AC_SUBST(ENABLE_STRIP)
|
||||||
AC_SUBST(USE_ELF_DYNSTR_GC)
|
AC_SUBST(USE_ELF_DYNSTR_GC)
|
||||||
|
|
|
@ -51,6 +51,10 @@ DIRS = \
|
||||||
themes \
|
themes \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
ifdef MOZ_AIRBAG
|
||||||
|
DIRS += airbag
|
||||||
|
endif
|
||||||
|
|
||||||
ifndef MINIMO
|
ifndef MINIMO
|
||||||
DIRS += \
|
DIRS += \
|
||||||
xre \
|
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
Двоичный файл не отображается.
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче