// balst_stacktraceutil.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALST_STACKTRACEUTIL #define INCLUDED_BALST_STACKTRACEUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide low-level utilities for obtaining & printing a stack-trace. // //@CLASSES: // balst::StackTraceUtil: utilities for 'balst::StackTrace' objects // //@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; // 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 // $ //.. #include <balscm_version.h> #include <balst_stacktrace.h> #include <bslma_allocator.h> #include <bsl_iosfwd.h> namespace BloombergLP { namespace balst { struct StackTraceUtil { // This 'struct' serves as a namespace for a collection of functions that // are useful for initializing and printing a stack-trace object. // CLASS METHODS static bsl::ostream& hexStackTrace(bsl::ostream &stream); // Write to the specified 'stream' the stack addresses from a stack // trace of the current thread, in hex from top to bottom, and return // 'stream'. static int loadStackTraceFromAddressArray( StackTrace *result, const void * const addresses[], int numAddresses, bool demanglingPreferredFlag = true); // Populate the specified 'result' with stack-trace information from // the stack, described by the specified array of 'addresses' of length // 'numAddresses'. Optionally specify 'demanglingPreferredFlag' to // indicate whether or not to attempt to perform demangling, however, // demangling is always performed on the Windows platform and never // performed on Solaris using the CC compiler regardless of the value // of 'demanglingPreferredFlag'. If 'demanglingPreferredFlag' is not // specified, demangling is performed on those platforms that support // it. Return 0 on success, and a non-zero value otherwise. Any // frames previously contained in the stack-trace object are discarded. // The behavior is undefined unless 'addresses' contains at least // 'numAddresses' addresses. Note that the return addresses from the // stack can be obtained by calling // 'bsls::StackAddressUtil::getStackAddresses', and that demangling // sometimes involves calling 'malloc'. static int loadStackTraceFromStack(StackTrace *result, int maxFrames = -1, bool demanglingPreferredFlag = true); // Populate the specified 'result' object with information about the // current thread's program stack. Optionally specify 'maxFrames' to // indicate the maximum number of frames to take from the top of the // stack. If 'maxFrames' is not specified, the default limit is at // least 1024. Optionally specify 'demanglingPreferredFlag' to // indicate whether to attempt to perform demangling, if possible. If // 'demanglingPreferredFlag' is not specfied, demangling is assumed to // be preferred, however, demangling is always performed on the Windows // platform and never performed on Solaris using the CC compiler // regardless of the value of 'demanglingPreferredFlag'. Any frames // previously contained in the 'stackTrace' object are discarded. // Return 0 on success, and a non-zero value otherwise. The behavior // is undefined unless 'maxFrames' (if specified) is greater than 0. // Note that demangling may involve calling 'malloc'. static bsl::ostream& printFormatted(bsl::ostream& stream, const StackTrace& stackTrace); // Stream the specified 'stackTrace' to the specified 'stream' in some // multi-line, human-readable format. Note that this operation // attempts to avoid using the default allocator. static bsl::ostream& printFormatted(bsl::ostream& stream, const StackTraceFrame& stackTraceFrame); // Write the value of the specified 'stackTraceFrame' to the specified // output 'stream' in some single-line, human-readable format and // return a reference to 'stream'. The name of the symbol is // represented by the 'symbolName' property of 'stackTraceFrame', if // known; otherwise, it is represented by the 'mangledSymbolName' // property, if known; otherwise, it is represented by "--unknown--". // Other frame attributes are written only if their values are known. // Note that the format is not fully specified, and can change without // notice. Also note that this method attempts to avoid using the // default allocator. static bsl::ostream& printHexStackTrace( bsl::ostream& stream, char delimiter = ' ', int maxFrames = -1, int additionalIgnoreFrames = 0, bslma::Allocator *allocator = 0); // Write to the specified 'stream' the stack addresses from a stack // trace of the current thread, in hex from top to bottom, and return // 'stream'. Optionally specify 'delimiter', that is to be written // between stack addresses. If 'delimiter' is not specified, the // addresses are separated by a single space. Optionally specify // 'maxFrames', the upper limit of the number of frames to obtain, // where a negative or unspecified value will be interpreted as a large // finite default value. Optionally specify 'additionalIgnoreFrames' // to be added to the number of frames from the stack top to be ignored // and not printed. Optionally specify 'allocator' to be used for // temporary storage; if none is specified, a locally created heap // bypass allocator will be used. The behavior is undefined unless // 'delimiter != 0' and 'additionalIgnoreFrames >= 0'. }; } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // 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. // ----------------------------- END-OF-FILE ----------------------------------