boinc/lib/mac/QBacktrace.h

755 lines
35 KiB
C

// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
/*
* QBacktrace.h
*
*/
/* This is part of a backtrace generator for boinc project applications.
*
* Adapted from Apple Developer Technical Support Sample Code QCrashReport
*
* This code handles Mac OS X 10.3.x through 10.4.9. It may require some
* adjustment for future OS versions; see the discussion of _sigtramp and
* PowerPC Signal Stack Frames in file QBacktrace.c.
*
* For useful tips on using backtrace information, see Apple Tech Note 2123:
* http://developer.apple.com/technotes/tn2004/tn2123.html#SECNOSYMBOLS
*
* To convert addresses to correct symbols, use the atos command-line tool:
* atos -o path/to/executable/with/symbols address
* Note: if address 1a23 is hex, use 0x1a23.
*
* To demangle mangled C++ symbols, use the c++filt command-line tool.
* You may need to prefix C++ symbols with an additonal underscore before
* passing them to c++filt (so they begin with two underscore characters).
*
* A very useful shell script to add symbols to a crash dump can be found at:
* http://developer.apple.com/tools/xcode/symbolizingcrashdumps.html
* Pipe the output of the shell script through c++filt to demangle C++ symbols.
*/
/*
File: QBacktrace.h
Contains: Code for generating backtraces.
Written by: DTS
Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved.
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or
redistribution of this Apple software constitutes acceptance of
these terms. If you do not agree with these terms, please do
not use, install, modify or redistribute this Apple software.
In consideration of your agreement to abide by the following
terms, and subject to these terms, Apple grants you a personal,
non-exclusive license, under Apple's copyrights in this
original Apple software (the "Apple Software"), to use,
reproduce, modify and redistribute the Apple Software, with or
without modifications, in source and/or binary forms; provided
that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the
following text and disclaimers in all such redistributions of
the Apple Software. Neither the name, trademarks, service marks
or logos of Apple Inc. may be used to endorse or promote
products derived from the Apple Software without specific prior
written permission from Apple. Except as expressly stated in
this notice, no other rights or licenses, express or implied,
are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or
by other works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis.
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Change History (most recent first):
$Log: QBacktrace.h,v $
Revision 1.1 2007/03/02 12:19:53
First checked in.
*/
#ifndef _QBACKTRACE_H
#define _QBACKTRACE_H
/////////////////////////////////////////////////////////////////
// System Interfaces
// Put <mach/mach.h> inside extern "C" guards for the C++ build
// because the Mach header files don't always have them.
#if defined(__cplusplus)
extern "C" {
#endif
#include <mach/mach.h>
#if defined(__cplusplus)
}
#endif
// Our interfaces
#include "QTaskMemory.h"
#include "QSymbols.h"
/////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif
/////////////////////////////////////////////////////////////////
/*!
@header QBacktrace.h
@abstract Comprehensive backtrace generation.
@discussion This module implements a number of backtrace routines:
o QBacktraceMachSelf does a backtrace of the current thread.
o QBacktraceMachThread does a backtrace of an arbitrary thread
within an arbitrary process.
All of the routines are implemented in terms of a common core.
The code is structured in a very generic way. For example,
on an Intel-based Macintosh computer, it's possible to
backtrace a PowerPC program (run using Rosetta) from a Intel
program, and vice versa.
Backtraces are inherently processor-specific. Internal to this
module is a ISA layer than adapts the backtrace for various ISA.
Currently it supports PowerPC (32-bit), PowerPC (64-bit),
Intel (32-bit), and Intel (64-bit).
If you're curious about how stack frames work for each ISA,
check out the comments in the implementation file. The comments
in the header focus on how you use these routines.
The core of this module is also (mostly) independent of the
technology that you use to read the address space of the
process that you're backtracing. The current implementation
uses Mach APIs to do this, but it is relatively simple to
retarget it to use some other API (a previous version of the
code read the memory directly and used the Carbon
Exception Manager to catch any exceptions that this triggered).
However, as we're currently quite wedded to Mach, I've only
retained the Mach functionality.
If you used a previous version of this code (known as
MoreBacktrace), please note the following changes:
o Everything has changed (-:
I completely rewrote this code to support multiple architectures.
By including support for 64-bit ISA, I was forced to
eliminate my dependencies on CoreServices (which isn't available
to 64-bit programs on Mac OS X 10.4.x), which means now I depend
solely on the System framework. Also, because I had to support
Intel, which requires Mach-O, I decided to drop support for CFM.
My theory is that anyone who wants to adopt the new version of
this module is doing so because they're porting to Intel, and
those folks have to leave their CFM build behind.
The good news is that the new implementation is very similar
in spirit to the old, and it should be very easy for you to
adopt the new code.
There are a number of approaches for using the routines exported
by this module.
o If you just want a simple backtrace, create an array of N
QBTFrame structures and just pass that into the function.
You'll get information about the N frames on the top of
the stack. Simple and easy.
o If you want to get all of the frames, you can first call
the routine with a NULL frame array. This will return
you the number of frames in the backtrace. You can use
that value to allocate a QBTFrame array with the appropriate
number of elements and then call the function again to
get the actual backtrace.
o You can also use a hybrid of these approaches. Start by
allocating an array with N entries, where N is likely to be
enough to accomodate a typical backtrace. Then call the
backtrace function. If it indicates that you missed some
elements, grow the array and call the backtrace function
again.
The backtrace functions generally don't fail with an error.
In general, if the chain of frames on the stack runs off the
rails, the backtrace function just returns a truncated backtrace.
The two situations where the backtrace function do return an
error are a) if it can't even start the backtrace, or
b) if something wacky happens, like it can't allocate memory,
or manipulate the target task, or find the target thread, and
so on.
If the backtrace function succeeds, you may need to dispose
of the symbol and library strings that it allocated by calling
QBacktraceDisposeSymbols. This is necessary when all of the
following are true:
o The backtrace function succeeds.
o You pass NULL to the symRef parameter.
o You pass a non-zero value to the frameArrayCount parameter
(and thus passed a non-NULL value to the frameArray
parameter).
To make things simpler, it's generally easiest to always
call QBacktraceDisposeSymbols. The module know which symbol
and library strings it allocated, and won't try to free anything
that it doesn't own. The QBacktraceDisposeSymbols is very
flexible about the parameters it accepts, which makes it easy
for you to call in all circumstances. See the function description
for more details.
The lifetime of the symbol and library strings returned by the
backtrace depends on the symRef parameter. If this is NULL, the
strings persist until you call QBacktraceDisposeSymbols. If it
is not NULL, the strings persist until you dispose of the symbols
object itself.
This module assumes that the state of the target task is stable;
if the task is still running, you may experienc odd artifacts
(as, for example, we take a backtrace of a thread then try
to find its symbols, only to determine that the Mach-O image
has been unloaded in the interim). The best way to prevent this
from happening is to suspend the task while you're accessing it.
The Mach routine task_suspend is very useful in this situation.
*/
/*!
@enum QBTFlags
@abstract Flags for a frame within a backtrace.
@discussion These flags provide information about a specific frame in
a backtrace.
Note: The code also uses this field to store some internal
flags. Do not be alarmed if you see bit other than the
ones shown below set in a QBTFlags value.
@constant kQBTFrameBadMask
The frame pointer of this frame is bad (for example, no
frame could be found or the frame pointer is misaligned or
outside of the stack or references unmapped memory).
IMPORTANT: This flag is set for the last frame in the
backtrace (where we've run off the end of the stack), but it
can also be set for intermediate frames (where we've detected
a frameless leaf routine, either at the top of the stack or
as part of crossing a signal frame).
@constant kQBTPCBadMask
The program counter of this frame is bad (for example, the
PC is misaligned or references unmapped memory).
@constant kQBTSignalHandlerMask
This frame represents the invocation of a signal handler.
*/
typedef int QBTFlags;
#define kQBTFrameBadMask 0x0001
#define kQBTPCBadMask 0x0002
#define kQBTSignalHandlerMask 0x0004
#if 0
enum QBTFlags {
kQBTFrameBadMask = 0x0001,
kQBTPCBadMask = 0x0002,
kQBTSignalHandlerMask = 0x0004
};
#endif
/*!
@struct QBTFrame
@abstract Describes a frame within a backtrace.
@discussion The end result of a backtrace is an array of QBTFrame
structures describing a particular frame in the backtrace.
IMPORTANT: The PC points to the code that's using the frame.
It is not the return address for that code. On architectures
where the frame holds the return address (Intel, but not
PowerPC), we do the appropriate corrections.
@field pc The PC for this function invocation.
@field fp The frame pointer for this function invocation.
@field flags Various flags; see QBTFlags above.
@field symbol Name of the symbol containing this PC. May be NULL.
IMPORTANT: The lifetime of this string is controlled by
various factors; see the discussion above for details.
@field library File path of the library containing this PC. May be NULL.
IMPORTANT: The lifetime of this string is controlled by
various factors; see the discussion above for details.
@field offset Offset from the symbol to the PC. Only valid if symbol
is not NULL.
*/
struct QBTFrame {
QTMAddr pc;
QTMAddr fp;
QBTFlags flags;
const char * symbol;
const char * library;
QTMOffset offset;
};
typedef struct QBTFrame QBTFrame;
/*!
@function QBacktraceMachSelf
@abstract Returns a backtrace of the current thread.
@discussion Returns a backtrace of the current thread in the array
described by frameArray and frameArrayCount. The number
of valid frames is returned in *frameCountPtr; this may
be larger than frameArrayCount.
IMPORTANT: You may need to call QBacktraceDisposeSymbols
on the resulting frames. See the detailed discussion of this
above.
@param symRef A symbols object for doing symbol to address translation
(and vice versa). This may be NULL, in which case the routine
will internally create the symbols object.
IMPORTANT: This parameter affects the lifecycle of the strings
returned in the frames array. See the discussion above for details.
@param frameArray
A pointer to an array of frame structures; this routine places
the backtrace into this array with the first element being the
most recent function invocation.
On entry, frameArray must not be NULL unless frameArrayCount
is zero.
On entry, if frameArray is not NULL, the contents of the array
are ignored.
On success, if frameArray is not NULL, then
MIN(frameArrayCount, *frameCountPtr) elements of the array
contain information. You must clean up these elements by
calling QBacktraceDisposeSymbols.
On error, you do not need to clean up this array.
@param frameArrayCount
The size of this array pointed to be frameArray. If this
is zero, frameArray may be NULL.
@param frameCountPtr
Returns the number of frames in the backtrace. On entry,
frameCountPtr must not be NULL and *frameCountPtr is ignored.
On success, *frameCountPtr contains the number frames in the
backtrace; this will not be zero. On error, *frameCountPtr
will be zero.
IMPORTANT: On success, *frameCountPtr may be greater than
frameArrayCount, in which case the backtrace contains more
frames than can be returned in frameArray. If you want to
get all of the frames, you can allocate a bigger frame array
and call this function again.
@result An errno-style error code per QTMErrnoFromMachError.
*/
extern int QBacktraceMachSelf(
QSymbolsRef symRef,
QBTFrame * frameArray,
size_t frameArrayCount,
size_t * frameCountPtr
);
/*!
@function QBacktraceMachThread
@abstract Returns a backtrace of an arbitrary thread in an arbitrary process.
@discussion Returns a backtrace of the specified thread in the specified
process. Places the backtrace in the array described by
frameArray and frameArrayCount. The number of valid frames
is returned in *frameCountPtr; this may be larger than
frameArrayCount.
IMPORTANT: You may need to call QBacktraceDisposeSymbols
on the resulting frames. See the detailed discussion of this
above.
@param task Must be the name of a valid send right for the task control
port of the process to inspect; mach_task_self is just fine.
If you do pass in mach_task_self, this routine automatically
enables some nice optimisations.
@param thread Must be the name of a valid send right for the thread control
port of the thread to inspect. Passing in mach_thread_self is
just fine, although you'd probably be better off calling
QBacktraceMachSelf instead.
@param cputype The CPU type of the dynamic linker from which you want to get
the symbols. Typically you would pass CPU_TYPE_ANY to use
the first dynamic linker that's discovered. See
QMOImageCreateFromTaskDyld for a detailed discussion of this
value.
@param symRef A symbols object for doing symbol to address translation
(and vice versa). This may be NULL, in which case the routine
will internally create the symbols object.
IMPORTANT: This parameter affects the lifecycle of the strings
returned in the frames array. See the discussion above for details.
@param stackBottom
This parameter, along with stackTop, defines the extent of the
stack you are tracing. If this information isn't handy, supply
0 for both parameters. Supplying meaningful values can reduce
the number of bogus frames reported if the stack is corrupt.
stackBottom and stackTop must both be zero, or stackBottom
must be strictly less than stackTop.
@param stackTop See the discussion of stackBottom.
@param frameArray
A pointer to an array of frame structures; this routine places
the backtrace into this array with the first element being the
most recent function invocation.
On entry, frameArray must not be NULL unless frameArrayCount
is zero.
On entry, if frameArray is not NULL, the contents of the array
are ignored.
On success, if frameArray is not NULL, then
MIN(frameArrayCount, *frameCountPtr) elements of the array
contain information. You must clean up these elements by
calling QBacktraceDisposeSymbols.
On error, you do not need to clean up this array.
@param frameArrayCount
The size of this array pointed to be frameArray. If this
is zero, frameArray may be NULL.
@param frameCountPtr
Returns the number of frames in the backtrace. On entry,
frameCountPtr must not be NULL and *frameCountPtr is ignored.
On success, *frameCountPtr contains the number frames in the
backtrace; this will not be zero. On error, *frameCountPtr
will be zero.
IMPORTANT: On success, *frameCountPtr may be greater than
frameArrayCount, in which case the backtrace contains more
frames than can be returned in frameArray. If you want to
get all of the frames, you can allocate a bigger frame array
and call this function again.
@result An errno-style error code per QTMErrnoFromMachError.
*/
extern int QBacktraceMachThread(
task_t task,
thread_t thread,
cpu_type_t cputype,
QSymbolsRef symRef,
QTMAddr stackBottom,
QTMAddr stackTop,
QBTFrame * frameArray,
size_t frameArrayCount,
size_t * frameCountPtr
);
/*!
@function QBacktraceMachThreadState
@abstract Returns a backtrace of a thread state within an arbitrary process.
@discussion Returns a backtrace of the specified thread state in the specified
process. Places the backtrace in the array described by
frameArray and frameArrayCount. The number of valid frames
is returned in *frameCountPtr; this may be larger than
frameArrayCount.
IMPORTANT: You may need to call QBacktraceDisposeSymbols
on the resulting frames. See the detailed discussion of this
above.
@param task Must be the name of a valid send right for the task control
port of the process to inspect; mach_task_self is just fine.
If you do pass in mach_task_self, this routine automatically
enables some nice optimisations.
@param stateFlavor
The thread state flavor of the thread state. For example,
you might pass in PPC_THREAD_STATE or x86_THREAD_STATE64.
@param state A pointer to a block of memory containing the thread state.
For example, if stateFlavor is PPC_THREAD_STATE, this would
point to a ppc_thread_state_t.
@param stateSize
The size of the thread state pointed to by the state parameter.
For example, if stateFlavor is PPC_THREAD_STATE, you could
pass in sizeof(ppc_thread_state_t). Equivalently, you could
pass in PPC_THREAD_STATE_COUNT * sizeof(integer_t).
@param cputype The CPU type of the dynamic linker from which you want to get
the symbols. Typically you would pass CPU_TYPE_ANY to use
the first dynamic linker that's discovered. See
QMOImageCreateFromTaskDyld for a detailed discussion of this
value.
@param symRef A symbols object for doing symbol to address translation
(and vice versa). This may be NULL, in which case the routine
will internally create the symbols object.
IMPORTANT: This parameter affects the lifecycle of the strings
returned in the frames array. See the discussion above for details.
@param stackBottom
This parameter, along with stackTop, defines the extent of the
stack you are tracing. If this information isn't handy, supply
0 for both parameters. Supplying meaningful values can reduce
the number of bogus frames reported if the stack is corrupt.
stackBottom and stackTop must both be zero, or stackBottom
must be strictly less than stackTop.
@param stackTop See the discussion of stackBottom.
@param frameArray
A pointer to an array of frame structures; this routine places
the backtrace into this array with the first element being the
most recent function invocation.
On entry, frameArray must not be NULL unless frameArrayCount
is zero.
On entry, if frameArray is not NULL, the contents of the array
are ignored.
On success, if frameArray is not NULL, then
MIN(frameArrayCount, *frameCountPtr) elements of the array
contain information. You must clean up these elements by
calling QBacktraceDisposeSymbols.
On error, you do not need to clean up this array.
@param frameArrayCount
The size of this array pointed to be frameArray. If this
is zero, frameArray may be NULL.
@param frameCountPtr
Returns the number of frames in the backtrace. On entry,
frameCountPtr must not be NULL and *frameCountPtr is ignored.
On success, *frameCountPtr contains the number frames in the
backtrace; this will not be zero. On error, *frameCountPtr
will be zero.
IMPORTANT: On success, *frameCountPtr may be greater than
frameArrayCount, in which case the backtrace contains more
frames than can be returned in frameArray. If you want to
get all of the frames, you can allocate a bigger frame array
and call this function again.
@result An errno-style error code per QTMErrnoFromMachError.
*/
extern int QBacktraceMachThreadState(
task_t task,
thread_state_flavor_t stateFlavor,
const void * state,
size_t stateSize,
cpu_type_t cputype,
QSymbolsRef symRef,
QTMAddr stackBottom,
QTMAddr stackTop,
QBTFrame * frameArray,
size_t frameArrayCount,
size_t * frameCountPtr
);
/*!
@function QBacktraceDisposeSymbols
@abstract Disposes of the symbol and library strings in a frame array.
It is safe to call this function on a frame array that is all
zeroes. It is safe to call this function on a frame array
that has been passed to any of the backtrace routines,
regardless of whether they succeeded or not.
@discussion Disposes of the symbol and library strings in the specified
frame array.
@param frameArray
A pointer to an array of frames with frameCount elements.
You may pass NULL, in which case the routine does nothing.
@param frameCount
The size of the array pointed to be frameArray.
*/
extern void QBacktraceDisposeSymbols(QBTFrame frameArray[], size_t frameCount);
/*!
@function QBTCreateThreadState
@abstract Gets the thread state of the specified thread.
@discussion Allocates a memory block and stores the thread state of the
specified thread into it.
To get this information, the routine must inspect the task
to see what architecture it's running. Doing this requires a
reference to an image object within the task. If you pass
in a symbols object, the routine will get it from that.
Otherwise, it will create its own temporary image object
internally, and to do that it needs you to tell it what
CPU type you're interested in.
@param task Must be the name of a valid send right for the task control
port of the process containing the thread; mach_task_self is
just fine.
@param thread Must be the name of a valid send right for the thread control
port of the thread whose state you want. Passing mach_thread_self
will work, although it won't yield useful results (because
the thread state will be for the thread as it entered the
kernel in the thread_get_state call, and that state is no
longer useful; specifically, the frames that the state refers
to have already been destroyed).
@param cputype This parameter is only consulted if the symRef is parameter
is NULL. In that case, the routine has to create an image
object for some Mach-O image inside the task and this value
controls which dynamic linker it finds. Typically you would
pass CPU_TYPE_ANY to use the first dynamic linker that's discovered.
See QMOImageCreateFromTaskDyld for a detailed discussion of this
value.
@param symRef A symbols object from which the routine can get an image object
for the dynamic linker within the task. This may be NULL,
in which case the routine will internally create its own
temporary image object.
@param stateFlavorPtr
Must not be NULL. On success, *stateFlavorPtr will contain
the thread state flavor for the thread state that's being
returned.
@param statePtr
Must not be NULL. On entry, *statePtr must be NULL. On
success, *statePtr will contain a pointer to a newly allocated
thread state block. The caller is responsible for freeing
that memory.
On error, *statePtr will be NULL.
@param stateSizePtr
Must not be NULL. On success, *stateSizePtr will contain
the size of the thread state that's being returned.
@result An errno-style error code per QTMErrnoFromMachError.
*/
extern int QBTCreateThreadState(
task_t task,
thread_t thread,
cpu_type_t cputype,
QSymbolsRef symRef,
thread_state_flavor_t * stateFlavorPtr,
void ** statePtr,
size_t * stateSizePtr
);
/*!
@function QBTCreateThreadStateSelf
@abstract Gets the thread state of the current thread.
@discussion Allocates a memory block and stores the thread state of the
current thread into it. This state contains only the
information needed to do a backtrace; this is architecture
dependent, but it is typically the stack pointer, frame pointer
and program counter. These correspond to the frame of the
caller of this routine.
IMPORTANT: This routine has the no-inline attribute because,
if you inlined it, you'd break its semantics.
@param stateFlavorPtr
Must not be NULL. On success, *stateFlavorPtr will contain
the thread state flavor for the thread state that's being
returned.
@param statePtr
Must not be NULL. On entry, *statePtr must be NULL. On
success, *statePtr will contain a pointer to a newly allocated
thread state block. The caller is responsible for freeing
that memory.
On error, *statePtr will be NULL.
@param stateSizePtr
Must not be NULL. On success, *stateSizePtr will contain
the size of the thread state that's being returned.
@result An errno-style error code per QTMErrnoFromMachError.
*/
extern int QBTCreateThreadStateSelf(
thread_state_flavor_t * stateFlavorPtr,
void ** statePtr,
size_t * stateSizePtr
) __attribute__((noinline));
#ifdef __cplusplus
}
#endif
#endif