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:
ted.mielczarek%gmail.com 2006-10-26 22:52:30 +00:00
Родитель e8e9061ce1
Коммит 72f0b0adcc
113 изменённых файлов: 48063 добавлений и 1 удалений

Просмотреть файл

@ -342,6 +342,10 @@ bin\extensions\inspector@mozilla.org\chrome\icons\default\winInspectorMain.ico
; [Additional Browsing Enhancements]
[abe]
; [airbag]
bin\crashreporter.exe
bin\crashreporter.ini
[talkback]
bin\extensions\talkback@mozilla.org\install.rdf
bin\extensions\talkback@mozilla.org\chrome.manifest

Просмотреть файл

@ -154,6 +154,7 @@ MOZ_MORKREADER = @MOZ_MORKREADER@
MOZ_NO_XPCOM_OBSOLETE = @MOZ_NO_XPCOM_OBSOLETE@
MOZ_NO_FAST_LOAD = @MOZ_NO_FAST_LOAD@
NS_PRINTING = @NS_PRINTING@
MOZ_AIRBAG = @MOZ_AIRBAG@
MOZ_JAVAXPCOM = @MOZ_JAVAXPCOM@
JAVA_INCLUDE_PATH="@JAVA_INCLUDE_PATH@"

Просмотреть файл

@ -256,7 +256,7 @@ _OBJS = \
$(JRI_STUB_CFILES) \
$(addsuffix .$(OBJ_SUFFIX), $(JMC_GEN)) \
$(CSRCS:.c=.$(OBJ_SUFFIX)) \
$(CPPSRCS:.cpp=.$(OBJ_SUFFIX)) \
$(patsubst %.cc,%.$(OBJ_SUFFIX),$(CPPSRCS:.cpp=.$(OBJ_SUFFIX))) \
$(CMSRCS:.m=.$(OBJ_SUFFIX)) \
$(CMMSRCS:.mm=.$(OBJ_SUFFIX)) \
$(ASFILES:.$(ASM_SUFFIX)=.$(OBJ_SUFFIX))

Просмотреть файл

@ -5491,6 +5491,23 @@ if test -n "${MOZ_JAVAXPCOM}"; then
fi
fi
dnl ========================================================
dnl = Airbag crash reporting (on by default on supported platforms)
dnl ========================================================
if test "$OS_ARCH" = "WINNT"; then
MOZ_AIRBAG=1
fi
MOZ_ARG_DISABLE_BOOL(airbag,
[ --disable-airbag Disable airbag crash reporting],
MOZ_AIRBAG=,
MOZ_AIRBAG=1)
if test -n "$MOZ_AIRBAG"; then
AC_DEFINE(MOZ_AIRBAG)
fi
dnl ========================================================
dnl = Enable compilation of specific extension modules
dnl ========================================================
@ -7580,6 +7597,7 @@ AC_SUBST(MOZ_SINGLE_PROFILE)
AC_SUBST(MOZ_SPELLCHECK)
AC_SUBST(MOZ_XPFE_COMPONENTS)
AC_SUBST(MOZ_USER_DIR)
AC_SUBST(MOZ_AIRBAG)
AC_SUBST(ENABLE_STRIP)
AC_SUBST(USE_ELF_DYNSTR_GC)

Просмотреть файл

@ -51,6 +51,10 @@ DIRS = \
themes \
$(NULL)
ifdef MOZ_AIRBAG
DIRS += airbag
endif
ifndef MINIMO
DIRS += \
xre \

Просмотреть файл

@ -0,0 +1,75 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Airbag integration
#
# The Initial Developer of the Original Code is
# Ted Mielczarek <ted.mielczarek@gmail.com>
# Portions created by the Initial Developer are Copyright (C) 2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = airbag
LIBXUL_LIBRARY = 1
LIBRARY_NAME = airbagexception_s
REQUIRES = \
xpcom \
string \
$(NULL)
DIRS = \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
DIRS += airbag/src/common/windows \
airbag/src/client/windows \
$(NULL)
endif
DIRS += client
LOCAL_INCLUDES = -I$(srcdir)/airbag/src
DEFINES += -DUNICODE -D_UNICODE
EXPORTS = \
nsAirbagExceptionHandler.h \
$(NULL)
CPPSRCS = \
nsAirbagExceptionHandler.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk

0
toolkit/airbag/airbag/AUTHORS Executable file
Просмотреть файл

0
toolkit/airbag/airbag/COPYING Executable file
Просмотреть файл

Просмотреть файл

236
toolkit/airbag/airbag/INSTALL Executable file
Просмотреть файл

@ -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.

189
toolkit/airbag/airbag/Makefile.am Executable file
Просмотреть файл

@ -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

1137
toolkit/airbag/airbag/Makefile.in Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

0
toolkit/airbag/airbag/NEWS Executable file
Просмотреть файл

2
toolkit/airbag/airbag/README Executable file
Просмотреть файл

@ -0,0 +1,2 @@
Airbag is a set of client and server components which implement a
crash-reporting system.

7239
toolkit/airbag/airbag/aclocal.m4 поставляемый Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1471
toolkit/airbag/airbag/autotools/config.guess поставляемый Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1599
toolkit/airbag/airbag/autotools/config.sub поставляемый Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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:

21294
toolkit/airbag/airbag/configure поставляемый Executable file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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> &parameters,
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> &parameters,
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> &parameters,
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> &parameters,
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> &parameters) {
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> &parameters,
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> &parameters,
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> &parameters);
// 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(&parameter_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__

0
toolkit/airbag/airbag/src/processor/testdata/minidump1.dmp поставляемый Executable file
Просмотреть файл

0
toolkit/airbag/airbag/src/processor/testdata/minidump1.out поставляемый Executable file
Просмотреть файл

0
toolkit/airbag/airbag/src/processor/testdata/minidump1.stack.out поставляемый Executable file
Просмотреть файл

Двоичные данные
toolkit/airbag/airbag/src/processor/testdata/minidump2.dmp поставляемый Executable file

Двоичный файл не отображается.

0
toolkit/airbag/airbag/src/processor/testdata/minidump2.sym поставляемый Executable file
Просмотреть файл

16
toolkit/airbag/airbag/src/processor/testdata/module1.out поставляемый 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 ^ =

16
toolkit/airbag/airbag/src/processor/testdata/module2.out поставляемый Executable file
Просмотреть файл

@ -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 ^ =

2
toolkit/airbag/airbag/src/processor/testdata/module3_bad.out поставляемый Executable file
Просмотреть файл

@ -0,0 +1,2 @@
FILE 1 file1.cc
FUNC 1000

74
toolkit/airbag/airbag/src/processor/testdata/test_app.cc поставляемый Executable file
Просмотреть файл

@ -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="&quot;$(VSInstallDir)\DIA SDK\include&quot;;..\..\.."
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="&quot;$(VSInstallDir)\DIA SDK\lib\diaguids.lib&quot;"
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="&quot;$(VSInstallDir)\DIA SDK\include&quot;;..\..\.."
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="&quot;$(VSInstallDir)\DIA SDK\lib\diaguids.lib&quot;"
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

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Двоичные данные
toolkit/airbag/airbag/src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb поставляемый Executable file

Двоичный файл не отображается.

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше