BDE 4.14.0 Production release
Loading...
Searching...
No Matches
balst_stacktraceutil

Detailed Description

Outline

Purpose

Provide low-level utilities for obtaining & printing a stack-trace.

Classes

See also
balst_stacktraceprintutil

Description

This component provides a namespace for functions used in obtaining and printing a stack-trace. Note that clients interested in simply printing a stack-trace are encouraged to use the balst_stacktraceprintutil component instead.

Usage

This section illustrates intended usage for this component.

The following examples demonstrate two distinct ways to load and print a stack-trace with balst::StackTraceUtil using (1) loadStackTraceFromStack and (2) loadStackTraceFromAddresses.

Example 1: Loading Stack-Trace Directly from the Stack

We start by defining a routine, recurseExample1, that will recurse the specified depth times, then call traceExample1:

int traceExample1(); // forward declaration
int recurseExample1(int *depth)
// Recurse the specified 'depth' number of times, then call
// 'traceExample1'.
{
int rc;
if (--*depth > 0) {
rc = recurseExample1(depth);
}
else {
rc = traceExample1();
}
if (rc) {
return rc; // RETURN
}
++*depth; // Prevent compiler from optimizing tail recursion as a
// loop.
return 0;
}

Then, we define the function traceExample1, that will print a stack-trace:

int traceExample1()
{

Now, we create a balst::StackTrace object and call loadStackTraceFrameStack to load the information from the stack of the current thread into the stack-trace object.

In this call to loadStackTraceFromStack, we use the default value of maxFrames, which is at least 1024 and the default value for demanglingPreferredFlag, which is true, meaning that the operation will attempt to demangle function names. Note that the object stackTrace takes very little room on the stack, and by default allocates most of its memory directly from virtual memory without going through the heap, minimizing potential complications due to stack-size limits and possible heap corruption.

balst::StackTrace stackTrace;
if (rc) { // Error handling is omitted.
return rc; // RETURN
}
Definition balst_stacktrace.h:206
static int loadStackTraceFromStack(StackTrace *result, int maxFrames=-1, bool demanglingPreferredFlag=true)

Finally, we use printFormatted to stream out the stack-trace, one frame per line, in a concise, human-readable format.

balst::StackTraceUtil::printFormatted(bsl::cout, stackTrace);
return 0;
}
static bsl::ostream & printFormatted(bsl::ostream &stream, const StackTrace &stackTrace)

The output from the preceding example on Solaris is as follows:

(0): traceExample1()+0x28 at 0x327d0 in balst_stacktraceutil.t.dbg_exc_mt
(1): recurseExample1(int*)+0x54 at 0x32e30 in balst_stacktraceutil.t.dbg_exc
(2): recurseExample1(int*)+0x44 at 0x32e20 in balst_stacktraceutil.t.dbg_exc
(3): recurseExample1(int*)+0x44 at 0x32e20 in balst_stacktraceutil.t.dbg_exc
(4): recurseExample1(int*)+0x44 at 0x32e20 in balst_stacktraceutil.t.dbg_exc
(5): recurseExample1(int*)+0x44 at 0x32e20 in balst_stacktraceutil.t.dbg_exc
(6): main+0x24c at 0x36c10 in balst_stacktraceutil.t.dbg_exc_mt
(7): _start+0x5c at 0x31d4c in balst_stacktraceutil.t.dbg_exc_mt

Notice that the lines have been truncated to fit this 79 column source file, and that on AIX or Windows, source file name and line number information will also be displayed.

Example 2: Loading a Stack-Trace from an Array of Stack Addresses

In this example, we demonstrate obtaining return addresses from the stack using bsls::StackAddressUtil, and later using them to load a balst::StackTrace object with a description of the stack. This approach may be desirable if one wants to quickly save the addresses that are the basis for a stack-trace, postponing the more time-consuming translation of those addresses to more human-readable debug information until later. To do this, we create an array of pointers to hold the return addresses from the stack, which may not be desirable if we are in a situation where there isn't much room on the stack.

First, we define a routine recurseExample2 which will recurse the specified depth times, then call traceExample2.

int traceExample2(); // forward declaration
int recurseExample2(int *depth)
// Recurse the specified 'depth' number of times, then call
// 'traceExample2', which will print a stack-trace.
{
int rc;
if (--*depth > 0) {
rc = recurseExample2(depth);
}
else {
rc = traceExample2();
}
if (rc) {
return rc; // RETURN
}
++*depth; // Prevent compiler from optimizing tail recursion as a
// loop.
return 0;
}
int traceExample2()
{

Then, within traceExample2, we create a stack-trace object and an array addresses to hold some addresses.

balst::StackTrace stackTrace;
enum { ARRAY_LENGTH = 50 };
void *addresses[ARRAY_LENGTH];

Next, we call bsls::StackAddressUtil::getStackAddresses to get the stored return addresses from the stack and load them into the array addresses. The call returns the number of addresses saved into the array, which will be less than or equal to ARRAY_LENGTH.

addresses,
ARRAY_LENGTH);
static int getStackAddresses(void **buffer, int maxFrames)

Then, we call loadStackTraceFromAddressArray to initialize the information in the stack-trace object, such as function names, source file names, and line numbers, if they are available. The optional argument, demanglingPreferredFlag, defaults to true.

&stackTrace,
addresses,
numAddresses);
if (rc) { // Error handling is omitted.
return rc; // RETURN
}
static int loadStackTraceFromAddressArray(StackTrace *result, const void *const addresses[], int numAddresses, bool demanglingPreferredFlag=true)

Finally, we can print out the stack-trace object using printFormatted, or iterate through the stack-trace frames, printing them out one by one. In this example, we want instead to output only function names, and not line numbers, source file names, or library names, so we iterate through the stack-trace frames and print out only the properties we want. Note that if a string is unknown, it is represented as "", here we print it out as "--unknown--" to let the user see that the name was unresolved.

for (int i = 0; i < stackTrace.length(); ++i) {
const balst::StackTraceFrame& frame = stackTrace[i];
const char *symbol = frame.isSymbolNameKnown()
? frame.symbolName().c_str()
: "--unknown__";
bsl::cout << '(' << i << "): " << symbol << endl;
}
return 0;
}
Definition balst_stacktraceframe.h:222
const bsl::string & symbolName() const
Definition balst_stacktraceframe.h:650
bool isSymbolNameKnown() const
Definition balst_stacktraceframe.h:693
int length() const
Return the number of stack-trace frames contained in this object.
Definition balst_stacktrace.h:435
const CHAR_TYPE * c_str() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:6705

Running this example would produce the following output:

(0): traceExample2()
(1): recurseExample2(int*)
(2): recurseExample2(int*)
(3): recurseExample2(int*)
(4): recurseExample2(int*)
(5): recurseExample2(int*)
(6): main
(7): _start

Example 3: Outputting a Hex Stack Trace

In this example, we demonstrate how to output return addresses from the stack to a stream in hex. Note that in this case the stack trace is never stored to a data object – when the operator<< is passed a pointer to the hexStackTrace function, it calls the hexStackTrace function, which gathers the stack addresses and immediately streams them out. After the operator<< is finished, the stack addresses are no longer stored anywhere.

First, we define a routine recurseExample3 which will recurse the specified depth times, then call traceExample3.

void traceExample3(); // forward declaration
void recurseExample3(int *depth)
// Recurse the specified 'depth' number of times, then call
// 'traceExample3', which will print a stack-trace.
{
if (--*depth > 0) {
recurseExample3(depth);
}
else {
traceExample3();
}
++*depth; // Prevent compiler from optimizing tail recursion as a
// loop.
}
void traceExample3()
{
// Now, within 'traceExample3', we output the stack addresses in hex by
// streaming the function pointer 'hexStackTrace' to the ostream:
}
static bsl::ostream & hexStackTrace(bsl::ostream &stream)

Finally, the output appears as a collection of hex values streamed out separated by spaces.

0x10001e438 0x10001e3b8 0x10001e3b0 0x10001e3b0 0x10001e3b0 0x10001e3b0 ...

The hex stack traces can be translated to a human-readable stack trace using tools outside of BDE, such as Bloomberg's /bb/bin/showfunc.tsk:

$ /bb/bin/showfunc.tsk <path to executable> 0x10001e438 0x10001e3b8 ...
0x10001e438 .traceExample3__Fv + 64
0x10001e3b8 .recurseExample3__FPi + 72
0x10001e3b0 .recurseExample3__FPi + 64
0x10001e3b0 .recurseExample3__FPi + 64
0x10001e3b0 .recurseExample3__FPi + 64
0x10001e3b0 .recurseExample3__FPi + 64
0x100000ba0 .main + 648
0x1000002fc .__start + 116
$