// 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
                           // =======================

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

    // 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
    explicit StackTracePrinter(int  maxFrames = -1,
                               bool demanglingPreferredFlag = true,
                               int  additionalIgnoreFrames = 0);
        // 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'.

#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.
};

bsl::ostream& operator<<(bsl::ostream&            stream,
                         const StackTracePrinter& object);
    // 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'.

}  // 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 ----------------------------------