// balst_stacktraceprinter.h                                          -*-C++-*-
#ifndef INCLUDED_BALST_STACKTRACEPRINTER
#define INCLUDED_BALST_STACKTRACEPRINTER

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

//@PURPOSE: Provide an object for streaming the current stack trace.
//
//@CLASSES:
//  balst::StackTracePrinter: object for streaming the current stack trace
//
//@SEE_ALSO: balst_stacktraceprintutil
//
//@DESCRIPTION: This component provides a mechanism,
// `balst::StackTracePrinter`, that when streamed (using a streaming operator)
// renders a description of the current call-stack to the supplied stream.
// This class can, at construction, be passed up to 3 optional arguments to
// drive the details with which the stack trace is to be performed.  Not all
// properties of a stack trace are printed on all platforms because the set of
// properties describing a stack trace that are obtainable varies according to
// both the platform and build parameters.  For example, on Solaris, source
// file names and line numbers are not provided.  Function names and addresses
// are provided on all platforms.  Streaming a `balst::StackTracePrinter`
// always prints a description of the stack of the calling thread.
//
///Usage
///-----
// In this section we show the intended usage of this component.
//
///Example 1: Streaming to BALL
/// - - - - - - - - - - - - - -
// First, we define a recursive function `recurseAndPrintStack` that recurses 4
// times, then calls `<< StackTracePrinter()` to obtain a stack trace and print
// it to `BALL_LOG_FATAL`:
// ```
// BALL_LOG_SET_NAMESPACE_CATEGORY("MY.CATEGORY");
//
// void recurseAndStreamStackDefault()
//     // Recurse 4 times and print a stack trace to 'BALL_LOG_FATAL'.
// {
//     static int recurseCount = 0;
//
//     if (recurseCount++ < 4) {
//         recurseAndStreamStackDefault();
//     }
//     else {
//         BALL_LOG_FATAL << balst::StackTracePrinter();
//     }
// }
// ```
// which, on Linux, produces the output:
// ```
// 02SEP2021_21:55:58.619290 21325 140602555295616 FATAL /bb/.../balst_stacktr
// aceprinter.t.cpp 733 UNINITIALIZED_LOGGER_MANAGER
// (0): recurseAndStreamStackDefault()+0xcf at 0x408acc source:balst_stacktrac
// eprinter.t.cpp:733 in balst_stacktraceprinter.t
// (1): recurseAndStreamStackDefault()+0x2a at 0x408a27 source:balst_stacktrac
// eprinter.t.cpp:735 in balst_stacktraceprinter.t
// (2): recurseAndStreamStackDefault()+0x2a at 0x408a27 source:balst_stacktrac
// eprinter.t.cpp:735 in balst_stacktraceprinter.t
// (3): recurseAndStreamStackDefault()+0x2a at 0x408a27 source:balst_stacktrac
// eprinter.t.cpp:735 in balst_stacktraceprinter.t
// (4): recurseAndStreamStackDefault()+0x2a at 0x408a27 source:balst_stacktrac
// eprinter.t.cpp:735 in balst_stacktraceprinter.t
// (5): main+0x1a7 at 0x408ff8 source:balst_stacktraceprinter.t.cpp:901 in bal
// st_stacktraceprinter.t
// (6): __libc_start_main+0xf5 at 0x7fe0943de495 in /lib64/libc.so.6
// (7): --unknown-- at 0x4074c5 in balst_stacktraceprinter.t
// ```
// Note that long lines of output here have been hand-wrapped to fit into
// comments in this 79-column source file.  Also note that if the full path of
// the executable or library is too long, only the basename will be displayed
// by the facility, while if it is short, then the full path will be displayed.
//
// Then we define a similar recursive function, except that when we construct
// the `StackTracePrinter` object, we pass 2 to the `maxFrames` argument,
// indicating, for some reason, that we want to see only the top two stack
// frames:
// ```
// void recurseAndStreamStackMaxFrames2()
//     // Recurse 4 times and print a stack trace to 'BALL_LOG_FATAL'.
// {
//     static int recurseCount = 0;
//
//     if (recurseCount++ < 4) {
//         recurseAndStreamStackMaxFrames2();
//     }
//     else {
//         BALL_LOG_FATAL << balst::StackTracePrinter(2);
//     }
// }
// ```
// which produces the output:
// ```
// 02SEP2021_21:55:58.624623 21325 140602555295616 FATAL /bb/.../balst_stacktr
// aceprinter.t.cpp 773 UNINITIALIZED_LOGGER_MANAGER
// (0): recurseAndStreamStackMaxFrames2()+0xcf at 0x408be1 source:balst_stackt
// raceprinter.t.cpp:773 in balst_stacktraceprinter.t
// (1): recurseAndStreamStackMaxFrames2()+0x2a at 0x408b3c source:balst_stackt
// raceprinter.t.cpp:775 in balst_stacktraceprinter.t
// ```
// Now, we define another similar recursive function, except that when we
// construct the `StackTracePrinter` object, we default `maxFrames` to a large
// value by passing it -1, and turn off demangling by passing `false` to the
// `damanglingPreferredFlag` argument:
// ```
// void recurseAndStreamStackNoDemangle()
//     // Recurse 4 times and print a stack trace to 'BALL_LOG_FATAL'.
// {
//     static int recurseCount = 0;
//
//     if (recurseCount++ < 4) {
//         recurseAndStreamStackNoDemangle();
//     }
//     else {
//         BALL_LOG_FATAL << balst::StackTracePrinter(-1, false);
//     }
// }
// ```
// which produces the output:
// ```
// 02SEP2021_21:55:58.636414 21325 140602555295616 FATAL /bb/.../balst_stacktr
// aceprinter.t.cpp 798 UNINITIALIZED_LOGGER_MANAGER
// (0): _Z31recurseAndStreamStackNoDemanglev+0xcf at 0x408cf6 source:balst_sta
// cktraceprinter.t.cpp:798 in balst_stacktraceprinter.t
// (1): _Z31recurseAndStreamStackNoDemanglev+0x2a at 0x408c51 source:balst_sta
// cktraceprinter.t.cpp:800 in balst_stacktraceprinter.t
// (2): _Z31recurseAndStreamStackNoDemanglev+0x2a at 0x408c51 source:balst_sta
// cktraceprinter.t.cpp:800 in balst_stacktraceprinter.t
// (3): _Z31recurseAndStreamStackNoDemanglev+0x2a at 0x408c51 source:balst_sta
// cktraceprinter.t.cpp:800 in balst_stacktraceprinter.t
// (4): _Z31recurseAndStreamStackNoDemanglev+0x2a at 0x408c51 source:balst_sta
// cktraceprinter.t.cpp:800 in balst_stacktraceprinter.t
// (5): main+0x1b1 at 0x409002 source:balst_stacktraceprinter.t.cpp:903 in bal
// st_stacktraceprinter.t
// (6): __libc_start_main+0xf5 at 0x7fe0943de495 in /lib64/libc.so.6
// (7): --unknown-- at 0x4074c5 in balst_stacktraceprinter.t
// ```
// Finally, we define another similar recursive function, except that we pass
// default values to the first 2 arguments of the `StackTracePrinter` and pass
// 5 to the third `additionalIgnoreFrames` argument.  This indicates a number
// of frames from the top of the stack to be ignored, which may be desired if
// the caller wants to do the streaming from within their own stack trace
// facility, in which case the top couple of frames would be within that stack
// trace facility, and unwanted and distracting for clients of that facility:
// ```
// void recurseAndStreamStackAddIgnore5()
//     // Recurse 4 times and print a stack trace to 'BALL_LOG_FATAL'.
// {
//     static int recurseCount = 0;
//
//     if (recurseCount++ < 4) {
//         recurseAndStreamStackAddIgnore5();
//     }
//     else {
//         BALL_LOG_FATAL << balst::StackTracePrinter(-1, true, 5);
//     }
// }
// ```
// which produces the output:
// ```
// 02SEP2021_21:55:58.647501 21325 140602555295616 FATAL /bb/.../balst_stacktr
// aceprinter.t.cpp 836 UNINITIALIZED_LOGGER_MANAGER
// (0): main+0x1b6 at 0x409007 source:balst_stacktraceprinter.t.cpp:904 in bal
// st_stacktraceprinter.t
// (1): __libc_start_main+0xf5 at 0x7fe0943de495 in /lib64/libc.so.6
// (2): --unknown-- at 0x4074c5 in balst_stacktraceprinter.t
// ```

#include <balscm_version.h>

#include <bsls_compilerfeatures.h>

#include <bsl_ostream.h>

namespace BloombergLP {
namespace balst {

                           // =======================
                           // class StackTracePrinter
                           // =======================

/// This `class` defines an object that, if streamed to a `bsl::ostream`,
/// will output a stack trace to that stream.
class StackTracePrinter {

    // PRIVATE TYPES
    enum { k_DEFAULT_MAX_FRAMES = 1024 };

    // DATA
    int         d_maxFrames;       // maximum # of frames to show

    bool        d_demanglingPreferredFlag;
                                   // whether symbols are to be demangled

    int         d_additionalIgnoreFrames;
                                   // the number of the frames on the top of
                                   // the stack that are not to be displayed,
                                   // in addition to
                                   // 'bsls::StackAddressUtil::k_IGNORE_FRAMES'

    // FRIENDS
    friend bsl::ostream& operator<<(bsl::ostream&, const StackTracePrinter&);

  private:
    // NOT IMPLEMENTED
    StackTracePrinter& operator=(const StackTracePrinter&);

  public:
    // CREATORS

    /// Create a `StackTracePrinter` object that will, if streamed, render a
    /// description of the current call stack to the stream.  Optionally
    /// specify `maxFrames` indicating maximum number of frames to dispay.
    /// If `maxFrames` is -1 or unspecified, use a value of at least 1024.
    /// The optionally specified `demanglingPreferredFlag` determines
    /// whether demangling occurs, on platforms where demangling is
    /// available and optional.  Optionally specify `additionalIgnoreFrames`
    /// which is added to `bsls::StackAddressUtil::k_IGNORE_FRAMES` to
    /// ignore the topmost frames of the caller.  Return a reference to
    /// `stream`.  The behavior is undefined unless `-1 == maxFrames` or
    /// `0 <= maxFrames`, and unless `0 <= additionalIgnoreFrames`.
    explicit StackTracePrinter(int  maxFrames = -1,
                               bool demanglingPreferredFlag = true,
                               int  additionalIgnoreFrames = 0);

#ifdef BSLS_COMPILERFEATURES_SUPPORT_DEFAULTED_FUNCTIONS
    StackTracePrinter(const StackTracePrinter& original) = default;
#endif
        // Create a copy of the specified 'original'.  Default the copy
        // constructor to work around a bug on the Aix compiler that generates
        // warnings every time an object of this type is passed to 'operator<<'
        // if this constructor is deleted or private.
};

/// Stream a newline followed by a multiline stack trace according to the
/// parameters passed to the specified `object` at its creation to the
/// specified `stream`.
bsl::ostream& operator<<(bsl::ostream&            stream,
                         const StackTracePrinter& object);

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

#endif

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