Provide low-level utilities for obtaining & printing a stack-trace.
More...
Namespaces |
namespace | balst |
Detailed Description
- Outline
-
-
- Purpose:
- Provide low-level utilities for obtaining & printing a stack-trace.
-
- Classes:
-
- See also:
- Component 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();
int recurseExample1(int *depth)
{
int rc;
if (--*depth > 0) {
rc = recurseExample1(depth);
}
else {
rc = traceExample1();
}
if (rc) {
return rc;
}
++*depth;
return 0;
}
Then, we define the function traceExample1
, that will print a stack-trace: 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. Finally, we use printFormatted
to stream out the stack-trace, one frame per line, in a concise, human-readable format. 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();
int recurseExample2(int *depth)
{
int rc;
if (--*depth > 0) {
rc = recurseExample2(depth);
}
else {
rc = traceExample2();
}
if (rc) {
return rc;
}
++*depth;
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
. 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
. 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. 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();
void recurseExample3(int *depth)
{
if (--*depth > 0) {
recurseExample3(depth);
}
else {
traceExample3();
}
++*depth;
}
void traceExample3()
{
bsl::cout << balst::StackTraceUtil::hexStackTrace << endl;
}
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
$