// balst_stacktrace.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_STACKTRACE
#define INCLUDED_BALST_STACKTRACE

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

//@PURPOSE: Provide a description of a function-call stack.
//
//@CLASSES:
//  balst::StackTrace: a description of a function-call stack
//
//@SEE_ALSO: balst_stacktraceframe, balst_stacktraceutil,
//           balst_stacktraceprintutil, bdlma_heapbypassallocator
//
//@DESCRIPTION: This component provides a (value-semantic) container class,
// 'balst::StackTrace', that is used to describe a function-call stack.  A
// stack-trace object contains a sequence of 'balst::StackTraceFrame' objects.
// By default, a 'balst::StackTrace' object is supplied memory by an owned
// 'bdlma::HeapBypassAllocator' object, though the client may specify another
// allocator at construction to be used in its place.
//
///Usage
///-----
// In this section we show the intended usage of this component.
//
///Example 1: Configuring a Stack-Trace Value
/// - - - - - - - - - - - - - - - - - - - - -
// In this example we demonstrate how to create a 'balst::StackTrace' object,
// and then to both modify and access its value.
//
// First, we set up a test allocator as default allocator.  A
// 'balst::StackTrace' object, by default, gets all its memory from an owned
// 'bdlma::HeapBypassAllocator' object.  To demonstrate this default behavior
// we start by setting the default allocator to a test allocator so we can
// verify later that it was unused:
//..
//  bslma::TestAllocator         da;
//  bslma::DefaultAllocatorGuard guard(&da);
//..
// Then, we create a stack-trace object.  Note that when we don't specify an
// allocator, the default allocator is not used -- rather, a heap-bypass
// allocator owned by the stack-trace object is used.  The heap-bypass
// allocator is recommended because this component is often used to obtain
// debug information in situations where an error has occurred, and the
// possibility of heap corruption can't be ruled out.  The heap-bypass
// allocator obtains its memory directly from virtual memory rather than going
// through the heap, avoiding potential complications due to heap corruption.
//..
//  balst::StackTrace stackTrace;
//  assert(0 == stackTrace.length());
//..
// Next, we 'resize' the stack-trace object to contain two default-constructed
// frames, and take references to each of the two new frames:
//..
//  stackTrace.resize(2);
//  assert(2 == stackTrace.length());
//  balst::StackTraceFrame& frame0 = stackTrace[0];
//  balst::StackTraceFrame& frame1 = stackTrace[1];
//..
// Then, we set the values of the fields of the two new frames.
//..
//  frame0.setAddress((void *) 0x12ab);
//  frame0.setLibraryFileName("/a/b/c/balst_stacktrace.t.dbg_exc_mt");
//  frame0.setLineNumber(5);
//  frame0.setOffsetFromSymbol(116);
//  frame0.setSourceFileName("/a/b/c/sourceFile.cpp");
//  frame0.setMangledSymbolName("_woof_1a");
//  frame0.setSymbolName("woof");
//
//  frame1.setAddress((void *) 0x34cd);
//  frame1.setLibraryFileName("/lib/libd.a");
//  frame1.setLineNumber(15);
//  frame1.setOffsetFromSymbol(228);
//  frame1.setSourceFileName("/a/b/c/secondSourceFile.cpp");
//  frame1.setMangledSymbolName("_arf_1a");
//  frame1.setSymbolName("arf");
//..
// Next, we verify the frames have the values we expect:
//..
//  assert((void *) 0x12ab               == frame0.address());
//  assert("/a/b/c/balst_stacktrace.t.dbg_exc_mt"
//                                       == frame0.libraryFileName());
//  assert(  5                           == frame0.lineNumber());
//  assert(116                           == frame0.offsetFromSymbol());
//  assert("/a/b/c/sourceFile.cpp"       == frame0.sourceFileName());
//  assert("_woof_1a"                    == frame0.mangledSymbolName());
//  assert("woof"                        == frame0.symbolName());
//
//  assert((void *) 0x34cd               == frame1.address());
//  assert("/lib/libd.a"                 == frame1.libraryFileName());
//  assert( 15                           == frame1.lineNumber());
//  assert(228                           == frame1.offsetFromSymbol());
//  assert("/a/b/c/secondSourceFile.cpp" == frame1.sourceFileName());
//  assert("_arf_1a"                     == frame1.mangledSymbolName());
//  assert("arf"                         == frame1.symbolName());
//..
// Next, we output the stack-trace object:
//..
//  stackTrace.print(cout, 1, 2);
//..
// Finally, we observe the default allocator was never used.
//..
//  assert(0 == da.numAllocations());
//..
// The above usage produces the following output:
//..
//  [
//    [
//      address = 0x12ab
//      library file name = "/a/b/c/balst_stacktrace.t.dbg_exc_mt"
//      line number = 5
//      mangled symbol name = "_woof_1a"
//      offset from symbol = 116
//      source file name = "/a/b/c/sourceFile.cpp"
//      symbol name = "woof"
//    ]
//    [
//      address = 0x34cd
//      library file name = "/lib/libd.a"
//      line number = 15
//      mangled symbol name = "_arf_1a"
//      offset from symbol = 228
//      source file name = "/a/b/c/secondSourceFile.cpp"
//      symbol name = "arf"
//    ]
//  ]
//..

#include <balscm_version.h>

#include <balst_stacktraceframe.h>

#include <bdlma_heapbypassallocator.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_review.h>

#include <bsl_iosfwd.h>
#include <bsl_vector.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bsl_algorithm.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace balst {

                              // ================
                              // class StackTrace
                              // ================

class StackTrace {
    // This value-semantic class describes a function-call stack, represented
    // as a sequence of randomly accessible 'StackTraceFrame' objects, each of
    // which represents one function call on the stack.  Note that if no
    // allocator is supplied at construction, an owned
    // 'bdlma::HeapBypassAllocator' object is used to supply memory.

    // DATA
    bdlma::HeapBypassAllocator   d_hbpAlloc;  // Used if no allocator is
                                              // supplied at construction.
                                              // Note this member must be
                                              // declared and constructed prior
                                              // to 'd_frames'.

    bsl::vector<StackTraceFrame> d_frames;    // sequence of stack-trace frames

    // FRIENDS
    friend bool operator==(const StackTrace&, const StackTrace&);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(StackTrace, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(StackTrace, bslmf::IsBitwiseMoveable);

    // CREATORS
    explicit
    StackTrace(bslma::Allocator *basicAllocator = 0);
        // Create an empty 'StackTrace' object (having a length of 0).
        // Optionally specify 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, then an owned heap-bypass allocator object is
        // used.  Note that the heap-bypass allocator is used by default to
        // avoid heap allocation in situations where the heap may have been
        // corrupted.

    StackTrace(const StackTrace&  original,
               bslma::Allocator  *basicAllocator = 0);
        // Create a 'StackTrace' object having the same value as the
        // specified 'original' object.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, then an owned
        // heap-bypass allocator object is used.  Note that the heap-bypass
        // allocator is used by default to avoid heap allocation in situations
        // where the heap may have been corrupted.

    //! ~StackTrace() = default;
        // Destroy this object.

    // MANIPULATORS
    StackTrace& operator=(const StackTrace& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    StackTraceFrame& operator[](int index);
        // Return a reference providing modifiable access to the stack-trace
        // frame at the specified 'index'.  The behavior is undefined unless
        // '0 <= index < length()'.

    void append(const StackTraceFrame& value);
        // Append to this sequence the specified 'value'.

    void removeAll();
        // Remove all stack-trace frames from this object.  After this
        // operation, the 'length()' method will return 0.

    void resize(int newLength);
        // Add default constructed stack-trace frames to, or remove stack-trace
        // frames from, the end of this stack-trace object such that, after the
        // operation, 'length() == newLength'.  Stack trace frames whose
        // indices are in the range '0 <= index < min(length, newLength)' will
        // be unchanged.  The behavior is undefined unless '0 <= newLength'.

                        // Aspects

    void swap(StackTrace& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    // ACCESSORS
    const StackTraceFrame& operator[](int index) const;
        // Return a reference providing non-modifiable access to the
        // stack-trace frame at the specified 'index'.  The behavior is
        // undefined unless '0 <= index < length()'.

    int length() const;
        // Return the number of stack-trace frames contained in this object.

                        // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the owned
        // heap-bypass allocator is used.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a reference to 'stream'.
        // Optionally specify an initial indentation 'level', whose absolute
        // value is incremented recursively for nested objects.  If 'level' is
        // specified, optionally specify 'spacesPerLevel', whose absolute value
        // indicates the number of spaces per indentation level for this and
        // all of its nested objects.  If 'level' is negative, suppress
        // indentation of the first line.  If 'spacesPerLevel' is negative,
        // format the entire output on one line, suppressing all but the
        // initial indentation (as governed by 'level').  If 'stream' is not
        // valid on entry, this operation has no effect.  Note that the format
        // is not fully specified, and can change without notice.
};

// FREE OPERATORS
bool operator==(const StackTrace& lhs, const StackTrace& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'StackTrace' objects have the
    // same value if they have the save length, and each of their corresponding
    // stack-trace frames have the same value.

bool operator!=(const StackTrace& lhs, const StackTrace& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'StackTrace' objects do
    // not have the same value if they do not have the same length, or any of
    // their corresponding stack-trace frames do not have the same value.

bsl::ostream& operator<<(bsl::ostream& stream, const StackTrace& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified and can change
    // without notice.  Also note that this method has the same behavior as
    // 'object.print(stream, 0, -1)', but with the attribute names elided.

// FREE FUNCTIONS
void swap(StackTrace& a, StackTrace& b);
    // Exchange the values of the specified 'a' and 'b' objects.  This function
    // provides the no-throw exception-safety guarantee if the two objects were
    // created with the same allocator and the basic guarantee otherwise.

// ============================================================================
//                        INLINE FUNCTION DEFINITIONS
// ============================================================================

                              // ----------------
                              // class StackTrace
                              // ----------------

// ACCESSORS
inline
bslma::Allocator *StackTrace::allocator() const
{
    return d_frames.get_allocator().mechanism();
}

// CREATORS
inline
StackTrace::StackTrace(bslma::Allocator *basicAllocator)
: d_hbpAlloc()
, d_frames(basicAllocator ? basicAllocator : &d_hbpAlloc)
{
}

inline
StackTrace::StackTrace(const StackTrace&  original,
                       bslma::Allocator  *basicAllocator)
: d_hbpAlloc()
, d_frames(original.d_frames,
           basicAllocator ? basicAllocator : &d_hbpAlloc)
{
}

// MANIPULATORS
inline
StackTrace& StackTrace::operator=(const StackTrace& rhs)
{
    d_frames = rhs.d_frames;

    return *this;
}

inline
StackTraceFrame& StackTrace::operator[](int index)
{
    BSLS_ASSERT(index >= 0);
    BSLS_ASSERT(index < length());

    return d_frames[index];
}

inline
void StackTrace::append(const StackTraceFrame& value)
{
    d_frames.push_back(value);
}

inline
void StackTrace::removeAll()
{
    d_frames.clear();
}

inline
void StackTrace::resize(int newLength)
{
    BSLS_ASSERT(newLength >= 0);

    d_frames.resize(newLength);
}

inline
void StackTrace::swap(StackTrace& other)
{
    // 'swap' is undefined for objects with non-equal allocators.

    BSLS_ASSERT(allocator() == other.allocator());

    d_frames.swap(other.d_frames);
}

// ACCESSORS
inline
const StackTraceFrame& StackTrace::operator[](int index) const
{
    BSLS_ASSERT(index >= 0);
    BSLS_ASSERT(index < length());

    return d_frames[index];
}

inline
int StackTrace::length() const
{
    return (int) d_frames.size();
}

}  // close package namespace

// FREE OPERATORS
inline
bool balst::operator==(const StackTrace& lhs, const StackTrace& rhs)
{
    return lhs.d_frames == rhs.d_frames;
}

inline
bool balst::operator!=(const StackTrace& lhs, const StackTrace& rhs)
{
    return !(lhs == rhs);
}

inline
bsl::ostream& balst::operator<<(bsl::ostream& stream, const StackTrace& object)
{
    object.print(stream, 0, -1);

    return stream;
}

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