Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component balst_stacktraceutil
[Package balst]

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:
balst::StackTraceUtil utilities for balst::StackTrace objects
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();    // 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;
      int rc = balst::StackTraceUtil::loadStackTraceFromStack(&stackTrace);

      if (rc) {  // Error handling is omitted.
          return rc;                                                // RETURN
      }
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;
  }
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.
      int numAddresses = bsls::StackAddressUtil::getStackAddresses(
                                                               addresses,
                                                               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.
      int rc = balst::StackTraceUtil::loadStackTraceFromAddressArray(
                                                               &stackTrace,
                                                               addresses,
                                                               numAddresses);

      if (rc) {  // Error handling is omitted.
          return rc;                                                // RETURN
      }
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;
  }
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:

      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
  $