// bsls_stackaddressutil.h                                            -*-C++-*-
#ifndef INCLUDED_BSLS_STACKADDRESSUTIL
#define INCLUDED_BSLS_STACKADDRESSUTIL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a utility for obtaining return addresses from the stack.
//
//@CLASSES:
//   bsls::StackAddressUtil: utilities for obtaining addresses from the stack
//
//@SEE_ALSO: balst_stacktraceutil
//
//@DESCRIPTION: This component defines a `struct`, `bsls::StackAddressUtil`,
// that provides a namespace for a function, `getStackAddresses`, that
// populates an array with an ordered sequence of return addresses from the
// current thread's function call stack.  Each return address points to the
// (text) memory location of the first instruction to be executed upon
// returning from a called routine.
//
// This component also provides a function, `formatCheapStack`, that builds a
// current stack trace and formats it with instructions on how to use
// `showfunc.tsk` to print out a stack trace matching where this function was
// called.  This is a Bloomberg standard "cheapstack" output.
//
///Usage
///-----
// In this section we show the intended usage of this component.
//
///Example 1: Obtaining Return Addresses and Verifying Their Validity
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In the following example we demonstrate how to obtain the sequence of
// function return addresses from the stack using `getStackAddresses`.
//
// First, we define `AddressEntry`, which will contain a pointer to the
// beginning of a function and an index corresponding to the function.  The `<`
// operator is defined so that a vector of address entries can be sorted in the
// order of the function addresses.  The address entries will be populated so
// that the entry containing `&funcN` when `N` is an integer will have an index
// of `N`.
// ```
// struct AddressEntry {
//     void *d_funcAddress;
//     int   d_index;
//
//     // CREATORS
//     AddressEntry(void *funcAddress, int index)
//     : d_funcAddress(funcAddress)
//     , d_index(index)
//         // Create an 'AddressEntry' object and initialize it with the
//         // specified 'funcAddress' and 'index'.
//     {}
//
//     bool operator<(const AddressEntry& rhs) const
//         // Return 'true' if the address stored in the object is lower than
//         // the address stored in 'rhs' and 'false' otherwise.  Note that
//         // this is a member function for brevity, it only exists to
//         // facilitate sorting 'AddressEntry' objects in a vector.
//     {
//         return d_funcAddress < rhs.d_funcAddress;
//     }
// };
// ```
// Then, we define `entries`, a vector of address entries.  This will be
// populated such that a given entry will contain function address `&funcN` and
// index `N`.  The elements will be sorted according to function address.
// ```
// bsl::vector<AddressEntry> entries;
// ```
// Next, we define `findIndex`:
// ```
// static int findIndex(const void *retAddress)
//     // Return the index of the address entry whose function uses an
//     // instruction located at specified 'retAddress'.  The behavior is
//     // undefined unless 'retAddress' is the address of an instruction in
//     // use by a function referred to by an address entry in 'entries'.
// {
//     unsigned int u = 0;
//     while (u < entries.size()-1 &&
//                                 retAddress >= entries[u+1].d_funcAddress) {
//         ++u;
//     }
//     assert(u < entries.size());
//     assert(retAddress >= entries[u].d_funcAddress);
//
//     int ret = entries[u].d_index;
//
//     if (veryVerbose) {
//         P_(retAddress) P_(entries[u].d_funcAddress) P(ret);
//     }
//
//     return ret;
// }
// ```
// Then, we define a volatile global variable that we will use in calculation
// to discourage compiler optimizers from inlining:
// ```
// volatile unsigned int volatileGlobal = 1;
// ```
// Next, we define a set of functions that will be called in a nested fashion
// -- `func5` calls `func4` who calls `fun3` and so on.  In each function, we
// will perform some inconsequential instructions to prevent the compiler from
// inlining the functions.
//
// Note that we know the `if` conditions in these 5 subroutines never evaluate
// to `true`, however, the optimizer cannot figure that out, and that will
// prevent it from inlining here.
// ```
// static unsigned int func1();
// static unsigned int func2()
// {
//     if (volatileGlobal > 10) {
//         return (volatileGlobal -= 100) * 2 * func2();             // RETURN
//     }
//     else {
//         return volatileGlobal * 2 * func1();                      // RETURN
//     }
// }
// static unsigned int func3()
// {
//     if (volatileGlobal > 10) {
//         return (volatileGlobal -= 100) * 2 * func3();             // RETURN
//     }
//     else {
//         return volatileGlobal * 3 * func2();                      // RETURN
//     }
// }
// static unsigned int func4()
// {
//     if (volatileGlobal > 10) {
//         return (volatileGlobal -= 100) * 2 * func4();             // RETURN
//     }
//     else {
//         return volatileGlobal * 4 * func3();                      // RETURN
//     }
// }
// static unsigned int func5()
// {
//     if (volatileGlobal > 10) {
//         return (volatileGlobal -= 100) * 2 * func5();             // RETURN
//     }
//     else {
//         return volatileGlobal * 5 * func4();                      // RETURN
//     }
// }
// static unsigned int func6()
// {
//     if (volatileGlobal > 10) {
//         return (volatileGlobal -= 100) * 2 * func6();             // RETURN
//     }
//     else {
//         return volatileGlobal * 6 * func5();                      // RETURN
//     }
// }
// ```
// Next, we define the macro FUNC_ADDRESS, which will take a parameter of
// `&<function name>` and return a pointer to the actual beginning of the
// function's code, which is a non-trivial and platform-dependent exercise.
// Note: this doesn't work on Windows for global routines.
// ```
// #if defined(BSLS_PLATFORM_OS_AIX)
// # define FUNC_ADDRESS(p) (((void **) (void *) (p))[0])
// #else
// # define FUNC_ADDRESS(p) ((void *) (p))
// #endif
// ```
// Then, we define `func1`, the last function to be called in the chain of
// nested function calls.  `func1` uses
// `bsls::StackAddressUtil::getStackAddresses` to get an ordered sequence of
// return addresses from the current thread's function call stack and uses the
// previously defined `findIndex` function to verify those address are correct.
// ```
// unsigned int func1()
//     // Call 'getAddresses' and verify that the returned set of addresses
//     // matches our expectations.
// {
// ```
// Next, we populate and sort the `entries` table, a sorted array of
// `AddressEntry` objects that will allow `findIndex` to look up within which
// function a given return address can be found.
// ```
//     entries.clear();
//     entries.push_back(AddressEntry(0, 0));
//     entries.push_back(AddressEntry(FUNC_ADDRESS(&func1), 1));
//     entries.push_back(AddressEntry(FUNC_ADDRESS(&func2), 2));
//     entries.push_back(AddressEntry(FUNC_ADDRESS(&func3), 3));
//     entries.push_back(AddressEntry(FUNC_ADDRESS(&func4), 4));
//     entries.push_back(AddressEntry(FUNC_ADDRESS(&func5), 5));
//     entries.push_back(AddressEntry(FUNC_ADDRESS(&func6), 6));
//     bsl::sort(entries.begin(), entries.end());
// ```
// Then, we obtain the stack addresses with `getStackAddresses`.
// ```
//     enum { BUFFER_LENGTH = 100 };
//     void *buffer[BUFFER_LENGTH];
//     bsl::memset(buffer, 0, sizeof(buffer));
//     int numAddresses = bsls::StackAddressUtil::getStackAddresses(
//                                                             buffer,
//                                                             BUFFER_LENGTH);
//     assert(numAddresses >= (int) entries.size());
//     assert(numAddresses < BUFFER_LENGTH);
//     assert(0 != buffer[numAddresses-1]);
//     assert(0 == buffer[numAddresses]);
// ```
// Finally, we go through several of the first addresses returned in `buffer`
// and verify that each address corresponds to the routine we expect it to.
//
// Note that on some, but not all, platforms there is an extra "narcissistic"
// frame describing `getStackAddresses` itself at the beginning of `buffer`.
// By starting our iteration through `buffer` at `k_IGNORE_FRAMES`, we
// guarantee that the first address we examine will be in `func1` on all
// platforms.
// ```
//     int funcIdx  = 1;
//     int stackIdx = bsls::StackAddressUtil::k_IGNORE_FRAMES;
//     for (; funcIdx < (int) entries.size(); ++funcIdx, ++stackIdx) {
//         assert(stackIdx < numAddresses);
//         assert(funcIdx == findIndex(buffer[stackIdx]));
//     }
//
//     if (testStatus || veryVerbose) {
//         Q(Entries:);
//         for (unsigned int u = 0; u < entries.size(); ++u) {
//             P_(u); P_((void *) entries[u].d_funcAddress);
//             P(entries[u].d_index);
//         }
//
//         Q(Stack:);
//         for (int i = 0; i < numAddresses; ++i) {
//             P_(i); P(buffer[i]);
//         }
//     }
//
//     return volatileGlobal;
// }
// ```
//
///Example 2: Obtaining a "Cheapstack"
///- - - - - - - - - - - - - - - - - -
// In this example we demonstrate how to use `formatCheapStack` to generate a
// string containing the current stack trace and instructions on how to print
// it out from `showfunc.tsk`.  Note that `showfunc.tsk` is a Bloomberg tool
// that, when given an executable along with a series of function addresses
// from a process that was running that executable, will print out a
// human-readable stack trace with the names of the functions being called in
// that stack trace.
//
// First, we define our function where we want to format the stack:
// ```
// struct MyTest {
//     static void printCheapStack()
//     {
//         char str[128];
//         bsls::StackAddressUtil::formatCheapStack(str, 128);
//         printf("%s", str);
//     }
// };
// ```
// Calling this function will then result in something like this being printed
// to standard output:
// ```
// Please run "/bb/bin/showfunc.tsk <binary_name_here> 403308 402641 ...
//                              ... 3710C1ED1D 400F49" to see the stack trace.
// ```
// Then, if you had encountered this output running the binary "mybinary.tsk",
// you could see your stack trace by running this command:
// ```
// /bb/bin/showfunc.tsk mybinary.tsk 403308 402641 3710C1ED1D 400F49
// ```
// This will produce output like this:
// ```
// 0x403308 _ZN6MyTest15printCheapStackEv + 30
// 0x402641 main + 265
// 0x3710c1ed1d ???
// 0x400f49 ???
// ```
// telling you that `MyTest::printCheapStack` was called directly from `main`.
// Note that if you had access to the binary name that was invoked, then that
// could be provided as the optional last argument to `printCheapStack` to get
// a `showfunc.tsk` command that can be more easily invoked, like this:
// ```
// struct MyTest2 {
//     static void printCheapStack()
//     {
//         char str[128];
//         bsls::StackAddressUtil::formatCheapStack(str, 128, "mybinary.tsk");
//         printf("%s", str);
//     }
// };
// ```
// resulting in output that looks like this:
// ```
// Please run "/bb/bin/showfunc.tsk mybinary.tsk 403308 402641 3710C1ED1D ...
//                                         ... 400F49" to see the stack trace.
// ```

#include <bsls_platform.h>

                      // ============================
                      // class bsls::StackAddressUtil
                      // ============================

namespace BloombergLP {
namespace bsls {

/// This struct provides a namespace for the function to obtain return
/// addresses from the stack.
class StackAddressUtil {

#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
    enum { k_SANITIZER_ADJUST = 1 };
#elif defined(__has_feature)
# if  __has_feature(address_sanitizer) || __has_feature(thread_sanitizer)     \
   || __has_feature(memory_sanitizer)
    enum { k_SANITIZER_ADJUST = 1 };
# else
    enum { k_SANITIZER_ADJUST = 0 };
# endif
#else
    enum { k_SANITIZER_ADJUST = 0 };
#endif

  public:

    // On some platforms, 'getStackAddresses' finds a frame representing
    // 'getStackAddresses' itself and possibly another frame representing
    // another function it calls.  These frames are usually unwanted.
    // 'k_IGNORE_FRAMES' instructs the caller as to whether the first N frames
    // are such unwanted frames.

#if   defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_DARWIN)
    enum { k_IGNORE_FRAMES = 1 + k_SANITIZER_ADJUST };
#else
    enum { k_IGNORE_FRAMES = 0 + k_SANITIZER_ADJUST };
#endif

    // CLASS METHODS

    /// Get an sequence of return addresses from the current thread's
    /// function call stack, ordered from most recent call to least recent,
    /// and load them into the specified array `*buffer`, which is at least
    /// the specified `maxFrames` in length.  A return address is an address
    /// stored on the stack that points to the first instruction that will
    /// be executed after the called subroutine returns.  If there are more
    /// than `maxFrames` frames on the stack, only the return addresses for
    /// the `maxFrames` most recent routine calls are stored.  When this
    /// routine completes, `buffer` will contain an ordered sequence of
    /// return addresses, sorted such that recent calls occur in the array
    /// before calls that took place before them.  Return the number of
    /// stack frames stored into `buffer` on success, and a negative value
    /// otherwise.  The behavior is undefined unless `maxFrames >= 0` and
    /// `buffer` has room for at least `maxFrames` addresses.  Note that
    /// this routine may fill `buffer` with garbage if the stack is corrupt,
    /// or on Windows if some stack frames represent optimized routines.
    static
    int getStackAddresses(void   **buffer,
                          int      maxFrames);

    /// Load the specified `output` buffer having the specified `length`
    /// with the Bloomberg standard "cheapstack" contents as a
    /// null-terminated string.  On successfully obtaining the current call
    /// stack, this will be instructions on how to run the Bloomberg tool
    /// `showfunc.tsk` (with the optionally specified `taskname`, otherwise
    /// with an attempt to obtain the system-specific process name) to get
    /// details of the current call stack where `formatCheapStack` was
    /// called.  On failure, text indicating that the call stack was not
    /// obtainable will be written to `output`.  If `length` is not long
    /// enough for the entire output it will be truncated.  The behavior is
    /// undefined unless `0 <= length` and `output` has the capacity for at
    /// least `length` bytes.
    static
    void formatCheapStack(char *output, int length, const char *taskname = 0);
};

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2018 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 ----------------------------------
