// balst_stacktracetestallocator.h                                    -*-C++-*-
#ifndef INCLUDED_BALST_STACKTRACETESTALLOCATOR
#define INCLUDED_BALST_STACKTRACETESTALLOCATOR

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

//@PURPOSE: Provide a test allocator that reports the call stack for leaks.
//
//@CLASSES:
//  balst::StackTraceTestAllocator: allocator that reports call stack for leaks
//
//@DESCRIPTION: This component provides an instrumented allocator,
// `balst::StackTraceTestAllocator`, that implements the
// `bdlma::ManagedAllocator` protocol.  An object of this type records the call
// stack for each allocation performed, and can report, either using the
// `reportBlocksInUse` method or implicitly at destruction, the call stack
// associated with every allocated block that has not (yet) been freed.  It is
// optionally supplied a `bslma::Allocator` at construction that it uses to
// allocate memory.
// ```
//                   ,------------------------------.
//                  ( balst::StackTraceTestAllocator  )
//                   `------------------------------'
//                                   |    ctor/dtor
//                                   |    numAllocations
//                                   |    allocationLimit
//                                   |    numBlocksInUse
//                                   |    reportBlocksInUse
//                                   |    setFailureHandler
//                                   V
//                        ,----------------------.
//                       ( bdlma::ManagedAllocator)
//                        `----------------------'
//                                   |    release
//                                   V
//                            ,----------------.
//                           ( bslma::Allocator )
//                            `----------------'
//                                        allocate
//                                        deallocate
// ```
// Note that allocation using a `balst::StackTraceTestAllocator` is
// deliberately incompatible with the default global `new`, `malloc`, `delete`,
// and `free`.  Using `delete` or `free` to free memory supplied by this
// allocator will corrupt the dynamic memory manager and also cause a memory
// leak (and will be reported by purify as freeing mismatched memory, freeing
// unallocated memory, or as a memory leak).  Using `deallocate` to free memory
// supplied by global `new` or `malloc` will immediately cause an error to be
// reported to the associated `ostream` and the abort handler (which can be
// configured to be a no-op) called.
//
///Overhead / Efficiency
///---------------------
// There is some overhead to using this allocator.  It's is slower than
// `bslma::NewDeleteAllocator` and uses more memory.  It is, however,
// comparable in speed and memory usage to `bslma::TestAllocator`.  The stack
// trace stored for each allocation is stack pointers only, which are compact
// and quick to obtain.  Actual resolving of the stack pointer to subroutine
// names and, on some platforms, source file names and line numbers, is
// expensive but doesn't happen during allocation or deallocation and is put
// off until a memory leak report is being generated.
//
// Note that the overhead increases and efficiency decreases as the
// `numRecordedFrames` argument to the constructor is increased.
//
///Failure Handler
///---------------
// An object of type `balst::StackTraceTestAllocator` always has a failure
// handler associated with it.  This is a configurable `bdef::Functton` object
// that will be called if any error condition is detected, after the error
// condition is reported.  By default, it is set to
// `balst::StackTraceTestAllocator::failAbort` which calls `abort`, but it may
// be set by `setFailureHandler` to another routine.  If the client does not
// want a core dump to occur, it is recommended they do:
// ```
//   stackTraceTestAllocator.setFailureHandler(
//                                 &balst::StackTraceTestAllocator::failNoop);
// ```
// The stack trace test allocator is prepared for the failure handler to
// return, throw (provided the client will catch the exception) or longjmp
// without undefined behavior.
//
// If a memory is found to be outstanding during destruction, that is
// considered a memory leak and a report is written.  After the report, the
// failure handler is called, and if the failure handler returns, the leaked
// memory is then released.  This means that if the failure handler throws or
// longjmps in this case, the leaked memory will not be freed, and there will
// be no way to release it afterward since the allocator will no longer exist.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// In this example, we will define a class `ShipsCrew` that does something,
// but leaks memory, and then we will demonstrate the use of the stack trace
// test allocator to locate the leak.
//
// First, we define `ShipsCrew`, a class that will read the names of a ship's
// crew from a file at construction, and make the results available through
// accessors:
// ```
// struct ShipsCrew {
//     // This struct will, at construction, read and parse a file describing
//     // the names of the crew of a ship.
//
//   private:
//     // PRIVATE TYPES
//     struct CharStrLess {
//         // Functor to compare two 'const char *'s in alphabetical order.
//
//         bool operator()(const char *a, const char *b) const
//         {
//             return bsl::strcmp(a, b) < 0;
//         }
//     };
//
//     typedef bsl::set<const char *, CharStrLess> NameSet;
//
//     // DATA
//     const char       *d_captain;
//     const char       *d_firstMate;
//     const char       *d_cook;
//     NameSet           d_sailors;
//     bslma::Allocator *d_allocator_p;
//
//   private:
//     // PRIVATE MANIPULATORS
//     void addSailor(const bsl::string& name);
//         // Add the specified 'name' to the set of sailor's names.
//         // Redundant names are ignored.
//
//     const char *copy(const bsl::string& str);
//         // Allocate memory for a copy of the specified 'str' as a char
//         // array, copy the contents of 'str' into it, and return a pointer
//         // to the new copy.
//
//     void free(const char **str);
//         // If '0 != str', deallocate '*str' using the allocator associated
//         // with this object and set '*str' to 0, otherwise do nothing.  The
//         // behavior is undefined if '0 == str'.
//
//     void setCaptain(const bsl::string& name);
//         // Set the name of the ship's captain to the specified 'name'.
//
//     void setCook(const bsl::string& name);
//         // Set the name of the ship's cook to the specified 'name'.
//
//     void setFirstMate(const bsl::string& name);
//         // Set the name of the ship's first mate to the specified 'name'.
//
//   public:
//     // CREATORS
//     explicit
//     ShipsCrew(const char        *crewFileName,
//               bslma::Allocator *basicAllocator = 0);
//         // Read the names of the ship's crew in from the file with the
//         // specified name 'crewFileName'.
//
//     ~ShipsCrew();
//         // Destroy this object and free memory.
//
//     // ACCESSORS
//     const char *captain();
//         // Return the captain's name.
//
//     const char *cook();
//         // Return the cook's name.
//
//     const char *firstMate();
//         // Return the first mate's name.
//
//     const char *firstSailor();
//         // Return the name of the sailor whose name is alphabetically the
//         // first in the list.
//
//     const char *nextSailor(const char *currentSailor);
//         // Return the next sailor alphabetically after the specified
//         // 'currentSailor', or 0 if 'currentSailor' is the last in the list
//         // or not found.  The behavior is undefined if
//         // '0 == currentSailor'.
// };
// ```
// Then, we implement the private manipulators of the class:
// ```
// PRIVATE MANIPULATORS
//  void ShipsCrew::addSailor(const bsl::string& name)
//  {
//      if (!d_sailors.count(name.c_str())) {
//          d_sailors.insert(copy(name));
//      }
//  }
//
//  const char *ShipsCrew::copy(const bsl::string& str)
//  {
//      char *result = (char *) d_allocator_p->allocate(str.length() + 1);
//      bsl::strcpy(result, str.c_str());
//      return result;
//  }
//
//  void ShipsCrew::free(const char **str)
//  {
//      assert(str);
//
//      if (*str) {
//          d_allocator_p->deallocate(const_cast<char *>(*str));
//          *str = 0;
//      }
//  }
//
//  void ShipsCrew::setCaptain(const bsl::string& name)
//  {
//      free(&d_captain);
//
//      d_captain = copy(name);
//  }
//
//  void ShipsCrew::setCook(const bsl::string& name)
//  {
//      free(&d_cook);
//
//      d_cook = copy(name);
//  }
//
//  void ShipsCrew::setFirstMate(const bsl::string& name)
//  {
//      free(&d_firstMate);
//
//      d_firstMate = copy(name);
//  }
// ```
// Next, we implement the creators:
// ```
// // CREATORS
// ShipsCrew::ShipsCrew(const char       *crewFileName,
//                      bslma::Allocator *basicAllocator)
// : d_captain(0)
// , d_firstMate(0)
// , d_cook(0)
// , d_sailors(    bslma::Default::allocator(basicAllocator))
// , d_allocator_p(bslma::Default::allocator(basicAllocator))
// {
//     bsl::ifstream input(crewFileName);
//     BSLS_ASSERT(!input.eof() && !input.bad());
//
//     bsl::string line(d_allocator_p);
//
//     while (bsl::getline(input, line), !line.empty()) {
//         bsl::size_t colon = line.find(':');
//         if (bsl::string::npos != colon) {
//             const bsl::string& field = line.substr(0, colon);
//             const bsl::string& name  = line.substr(colon + 1);
//
//             if      (0 == bdlb::String::lowerCaseCmp(field, "captain")) {
//                 setCaptain(name);
//             }
//             else if (0 == bdlb::String::lowerCaseCmp(field, "first mate")){
//                 setFirstMate(name);
//             }
//             else if (0 == bdlb::String::lowerCaseCmp(field, "cook")) {
//                 setCook(name);
//             }
//             else if (0 == bdlb::String::lowerCaseCmp(field, "sailor")) {
//                 addSailor(name);
//             }
//             else {
//                 cerr << "Unrecognized field '" << field << "' in line '" <<
//                                                              line << "'\n";
//             }
//         }
//         else {
//             cerr << "Garbled line '" << line << "'\n";
//         }
//     }
// }
//
// ShipsCrew::~ShipsCrew()
// {
//     free(&d_captain);
//     free(&d_firstMate);
//
//     // Note that deallocating the strings will invalidate 'd_sailors' --
//     // any manipulation of 'd_sailors' other than destruction after this
//     // would lead to undefined behavior.
//
//     const NameSet::iterator end = d_sailors.end();
//     for (NameSet::iterator it = d_sailors.begin(); end != it; ++it) {
//         d_allocator_p->deallocate(const_cast<char *>(*it));
//     }
// }
// ```
// Then, we implement the public accessors:
// ```
// ACCESSORS
//  const char *ShipsCrew::captain()
//  {
//      return d_captain ? d_captain : "";
//  }
//
//  const char *ShipsCrew::cook()
//  {
//      return d_cook ? d_cook : "";
//  }
//
//  const char *ShipsCrew::firstMate()
//  {
//      return d_firstMate ? d_firstMate : "";
//  }
//
//  const char *ShipsCrew::firstSailor()
//  {
//      NameSet::iterator it = d_sailors.begin();
//      return d_sailors.end() == it ? 0 : *it;
//  }
//
//  const char *ShipsCrew::nextSailor(const char *currentSailor)
//  {
//      assert(currentSailor);
//      NameSet::iterator it = d_sailors.find(currentSailor);
//      if (d_sailors.end() != it) {
//          ++it;
//      }
//      return d_sailors.end() == it ? 0 : *it;
//  }
// ```
// Next, in `main`, we create our file `./shipscrew.txt` describing the crew of
// the ship.  Note that the order of crew members is not important:
// ```
//     {
//         bsl::ofstream outFile("shipscrew.txt");
//
//         outFile << "sailor:Mitch Sandler\n"
//                 << "sailor:Ben Lampert\n"
//                 << "cook:Bob Jones\n"
//                 << "captain:Steve Miller\n"
//                 << "sailor:Daniel Smith\n"
//                 << "first mate:Sally Chandler\n"
//                 << "sailor:Joe Owens\n";
//     }
// ```
// Then, we set up a test case to test our `ShipsCrew` class.  We do not use
// the stack trace test allocator yet, we just use a `bslma::TestAllocator` to
// get memory usage statistics and determine whether any leakage occurred.
// ```
//     {
//         bslma::TestAllocator ta("Bslma Test Allocator");
//         bslma::TestAllocatorMonitor tam(&ta);
//
//         {
//             ShipsCrew crew("shipscrew.txt", &ta);
//             assert(tam.isInUseUp());
//             assert(tam.isTotalUp());
//
//             if (verbose) {
//                 cout << "Captain: "  << crew.captain() <<
//                     "\nFirst Mate: " << crew.firstMate() <<
//                     "\nCook: "       << crew.cook() << endl;
//                 for (const char *sailor = crew.firstSailor(); sailor;
//                                         sailor = crew.nextSailor(sailor)) {
//                     cout << "Sailor: " << sailor << endl;
//                 }
//             }
//         }
//
//         int bytesLeaked = ta.numBytesInUse();
//         if (bytesLeaked > 0) {
//             cout << bytesLeaked << " bytes of memory were leaked!\n";
//         }
//     }
// ```
// The program generates the following output in non-verbose mode, telling us
// that one segment of 10 bytes was leaked:
// ```
// 10 bytes of memory were leaked!
// MEMORY_LEAK from Bslma Test Allocator:
// Number of blocks in use = 1
// Number of bytes in use = 10
// ```
// Next, we would like to use stack trace test allocator to tell us WHERE the
// memory leak is, but we have a problem: our test case not only uses
// `bslma::TestAllocator`, but it calls the `numBytesInUse` accessor, which is
// not available from stack trace test allocator.  We are also using
// `bslma::TestAllocatorMonitor`, which will only work with
// `bslma::TestAllocator`.  So if we were to just substitute the stack trace
// test allocator for the bslma test allocator, it would break our test case in
// several ways.  To instrument our test with a minimal change to the code, we
// create a stack test test allocator and feed that allocator to the
// constructor to bslma test allocator.  The rest of our example will now work
// without modification.  (Note that it is important to call
// `ta.setNoAbort(true)` when we use this method, otherwise the bslma test
// allocator will bomb out before the destructor for `stta` is able to generate
// its report).
// ```
//     {
//         balst::StackTraceTestAllocator stta;
//         stta.setName("stta");
//         stta.setFailureHandler(&stta.failNoop);
//
//         bslma::TestAllocator ta("Bslma Test Allocator", &stta);
//         ta.setNoAbort(true);
//
//         // the rest of the test case after this is totally unchanged
//
//         bslma::TestAllocatorMonitor tam(&ta);
//
//         {
//             ShipsCrew crew("shipscrew.txt", &ta);
//             assert(tam.isInUseUp());
//             assert(tam.isTotalUp());
//
//             if (verbose) {
//                 cout << "Captain: "  << crew.captain() <<
//                     "\nFirst Mate: " << crew.firstMate() <<
//                     "\nCook: "       << crew.cook() << endl;
//                 for (const char *sailor = crew.firstSailor(); sailor;
//                                         sailor = crew.nextSailor(sailor)) {
//                     cout << "Sailor: " << sailor << endl;
//                 }
//             }
//         }
//
//         int bytesLeaked = ta.numBytesInUse();
//         if (bytesLeaked > 0) {
//             cout << bytesLeaked << " bytes of memory were leaked!\n";
//         }
//     }
// ```
// Now, this generates the following report:
// ```
// 10 bytes of memory were leaked!
// MEMORY_LEAK from Bslma Test Allocator:
//   Number of blocks in use = 1
//    Number of bytes in use = 10
// ===========================================================================
// Error: memory leaked:
// 1 block(s) in allocator 'stta' in use.
// Block(s) allocated from 1 trace(s).
// ---------------------------------------------------------------------------
// Allocation trace 1, 1 block(s) in use.
// Stack trace at allocation time:
// (0): BloombergLP::balst::StackTraceTestAllocator::allocate(int)+0x17d at 0x
// 805e741 in balst_stacktracetestallocator.t.dbg_exc_mt
// (1): BloombergLP::bslma::TestAllocator::allocate(int)+0x12c at 0x8077398 in
//  balst_stacktracetestallocator.t.dbg_exc_mt
// (2): ShipsCrew::copy(bsl::basic_string<char, std::char_traits<char>, bsl::a
// llocator<char> > const&)+0x31 at 0x804c3db in balst_stacktracetestallocator
// .t.dbg_exc_mt
// (3): ShipsCrew::setCook(bsl::basic_string<char, std::char_traits<char>, bsl
// ::allocator<char> > const&)+0x2d at 0x804c4c1 in balst_stacktracetestalloca
// tor.t.dbg_exc_mt
// (4): ShipsCrew::ShipsCrew(char const*, BloombergLP::bslma::Allocator*)+0x23
// 4 at 0x804c738 in balst_stacktracetestallocator.t.dbg_exc_mt
// (5): main+0x53c at 0x804d55e in balst_stacktracetestallocator.t.dbg_exc_mt
// (6): __libc_start_main+0xdc at 0x182e9c in /lib/libc.so.6
// (7): --unknown-- at 0x804c1d1 in balst_stacktracetestallocator.t.dbg_exc_mt
// ```
// Finally, Inspection shows that frame (3) of the stack trace from the
// allocation of the leaked segment was from `ShipsCrew::setCook`.  Inspection
// of the code shows that we neglected to free `d_cook` in the destructor and
// we can now easily fix our leak.

#include <balscm_version.h>

#include <bslmt_mutex.h>

#include <bdlma_managedallocator.h>

#include <bslma_allocator.h>

#include <bsls_atomic.h>
#include <bsls_keyword.h>

#include <bsl_cstddef.h>
#include <bsl_functional.h>
#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace balst {

                       // =============================
                       // class StackTraceTestAllocator
                       // =============================

/// This class defines a concrete "test" allocator mechanism that implements
/// the `bdlma::ManagedAllocator` protocol, and provides instrumentation to
/// track the set of all blocks allocated by this allocator that have yet to
/// be freed.  At any time it can produce a report about such blocks,
/// listing for each place that any unfreed blocks were allocated
/// * the number of unfreed blocks allocated at that place
/// * the stack trace at that place
/// The allocator will also detect redundant frees of the same block, and
/// frees by the wrong allocator.  The client can choose whether such
/// violations are handled by a core dump, or merely a report being written.
///
/// Note that, unlike many other allocators, this allocator does DOES NOT
/// rely on the currently installed default allocator (see `bslma_default`)
/// at all, but instead -- by default -- uses `MallocFreeAllocator`
/// singleton, which in turn calls the C Standard Library functions `malloc`
/// and `free` as needed.  Clients may, however, override this allocator by
/// supplying (at construction) any other allocator implementing the
/// `bslma::Allocator` protocol.
class StackTraceTestAllocator : public bdlma::ManagedAllocator {

  public:
    // PUBLIC TYPES
    typedef bsl::function<void()> FailureHandler;
                // Type of functor called by this object to handle failures.
                // Note that this can be set and accessed using th
                // 'setFailureHandler' and 'failureHandler' methods
                // respectively.

  private:
    // PRIVATE TYPES
    enum AllocatorMagic { k_STACK_TRACE_TEST_ALLOCATOR_MAGIC = 1335775331 };

    struct BlockHeader;                            // information stored in
                                                   // each block (defined in
                                                   // .cpp)

    // DATA
    AllocatorMagic            d_magic;             // magic # to identify type
                                                   // of memory allocator

    bsls::AtomicInt           d_numBlocksInUse;    // number of currently
                                                   // allocated blocks, unfreed

    bsls::AtomicInt64         d_numAllocations;    // number of allocations
                                                   // that have occurred since
                                                   // creation

    bsls::AtomicInt64         d_allocationLimit;   // number of allocations
                                                   // before exception is
                                                    // thrown by this object

    BlockHeader              *d_blocks;            // list of currently
                                                   // allocated, unfreed blocks

    mutable bslmt::Mutex      d_mutex;             // mutex used to synchronize
                                                   // access to this object

    const char               *d_name;              // name of this allocator

    FailureHandler            d_failureHandler;    // function we are to call
                                                   // on errors.  The default
                                                   // handler will call
                                                   // 'abort'.

    const int                 d_maxRecordedFrames; // max number of stack trace
                                                   // frames to store in each
                                                   // block; may be larger than
                                                   // the number of frames
                                                   // requested to the ctor due
                                                   // to ignored frames

    const int                 d_traceBufferLength; // length, in pointers, of
                                                   // area for storing stack
                                                   // traces; may be larger
                                                   // than
                                                   // 'd_maxRecordedFrames' due
                                                   // to alignment
                                                   // considerations

    bsl::ostream             *d_ostream;           // stream to which
                                                   // diagnostics are to be
                                                   // written; held, not owned

    bool                      d_demangleFlag;      // if 'true', demangling of
                                                   // symbol names is attempted

    bslma::Allocator         *d_allocator_p;       // held, not owned

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

  private:
    // PRIVATE ACCESSORS

    /// Return 0 if the block specified by `blockHdr` was allocated with
    /// this allocator, is not corrupted, and has not yet been freed; report
    /// diagnostics to `*d_ostream` and return a non-zero value otherwise.
    int checkBlockHeader(const BlockHeader *blockHdr) const;

  public:
    // CLASS METHODS

    /// Calls `bsl::abort()`, `d_failureHandler` is initialized to this
    /// value by all constructors.  Note that in ALL failure situations,
    /// errors or warnings will be written to the `ostream` associated with
    /// this object prior to the failure handler call.
    static void failAbort();

    /// Does nothing.  `setFailureHandler` may be called with this function,
    /// in which case this allocator object, when a failure occurs, will
    /// recover rather than abort.  Note that in ALL failure situations,
    /// errors or warnings will be written to the `ostream` associated with
    /// this object prior to the failure handler call.
    static void failNoop();

    // CREATORS

    /// Create a test allocator.  Optionally specify `numRecordedFrames`,
    /// the number of stack trace frame pointers to be saved for every
    /// allocation.  Specifying a larger value of `numRecordedFrames` means
    /// that stack traces, when given, will be more complete, but will also
    /// mean that both more CPU time and more memory per allocation will be
    /// consumed.  If `numRecordedFrames` is not specified, a value of `12`
    /// will be assumed.  Optionally specify `basicAllocator`, the allocator
    /// from which memory will be provided.  If `basicAllocator` is 0, the
    /// `MallocFreeAllocator` singleton is used.  Associate `bsl::cerr` with
    /// this object for error diagnostic output, which may be changed by
    /// calling the `setOstream` manipulator.  Set the
    /// `demanglingPreferringFlag` attribute to `true`, which may be changed
    /// using the `setDemanglingPreferredFlag` manipulator.  The behavior is
    /// undefined if `numRecordedFrames < 2`.
    explicit
    StackTraceTestAllocator(bslma::Allocator *basicAllocator = 0);
    explicit
    StackTraceTestAllocator(int               numRecordedFrames,
                            bslma::Allocator *basicAllocator = 0);

    /// Destroy this allocator.  Report any memory leaks to the `ostream`
    /// that was supplied at construction.  If no memory leaks are observed,
    /// nothing is written to the output `ostream`.  Call the failure
    /// handler if `numBlocksInUse() > 0`.  Note that a report of
    /// outstanding memory blocks is written to `ostream` before the failure
    /// handler is called, and if the failure handler returns, all
    /// outstanding memory blocks will be released.
    ~StackTraceTestAllocator() BSLS_KEYWORD_OVERRIDE;

    // MANIPULATORS

    /// Return a newly allocated block of memory of the specified positive
    /// `size` (in bytes).  If `size` is 0, a null pointer is returned with
    /// no other other effect.  Otherwise, invoke the `allocate` method of
    /// the allocator supplied at construction and record the returned block
    /// in order to be able to report leaked blocks upon destruction.
    void *allocate(size_type size) BSLS_KEYWORD_OVERRIDE;

    /// Return the memory block at the specified `address` back to this
    /// allocator.  If `address` is 0, this function has no effect.
    /// Otherwise, if the memory at `address` is consistent with being
    /// allocated from this test allocator, deallocate it using the
    /// underlying allocator and delete it from the data structures keeping
    /// track of blocks in use'.  If `address` is not zero and is not the
    /// address of a block allocated with this allocator (or if it is being
    /// deallocated a second time), write an error message and call the
    /// failure handler.
    void deallocate(void *address) BSLS_KEYWORD_OVERRIDE;

    /// Deallocate all memory held by this allocator.
    void release() BSLS_KEYWORD_OVERRIDE;

    /// Set the number of valid allocation requests before an exception is
    /// to be thrown for this allocator to the specified `limit`.  If
    /// `limit` is less than 0, no exception is to be thrown.  By default,
    /// no exception is scheduled.
    void setAllocationLimit(bsls::Types::Int64 limit);

    /// Set the `demanglingPreferredFlag` attribute, which is used to
    /// determine whether demangling of symbols is to be attempted when
    /// generating diagnostics, to the specified `value`.  The default value
    /// of the flag is `true`.  However the flag is ignored on some
    /// platforms; demangling never happens on some platforms and always
    /// happens on others.
    void setDemanglingPreferredFlag(bool value);

    /// Set the failure handler associated with this allocator object to the
    /// specified `func`.  Upon construction, the function `failAbort` is
    /// associated with this object by default.  Note that `func` will be
    /// called by this object's destructor if memory is leaked, so it is
    /// important that it not throw.  Note that in ALL failure situations,
    /// errors or warnings will be written to the `ostream` associated with
    /// this object prior to the call to the failure handler.
    void setFailureHandler(const FailureHandler& func);

    /// Set the name of this allocator to the specified `name`.  If
    /// `setName` is never called, the name of the allocator is "<unnamed>".
    /// Note that the lifetime of `name` must exceed the lifetime of this
    /// object.
    void setName(const char * name);

    /// Set the stream to which diagnostics will be written to the specified
    /// `ostream`.  If `setOstream` is never called, diagnostics will be
    /// written to `bsl::cerr`.
    void setOstream(bsl::ostream *ostream);

    // ACCESSORS

    /// Return the current number of allocation requests left before an
    /// exception is thrown.  A negative value indicates that no exception
    /// is scheduled.
    bsls::Types::Int64 allocationLimit() const;

    /// Return a reference to the function that will be called when a
    /// failure is observed.
    const FailureHandler& failureHandler() const;

    /// Return the number of allocations from this object that have occurred
    /// since creation.  Note that this does not count allocations of 0
    /// length, which return null pointers.
    bsls::Types::Int64 numAllocations() const;

    /// Return the number of blocks currently allocated from this object.
    bsl::size_t numBlocksInUse() const;

    /// Write a report to the specified `ostream`, reporting the unique
    /// call-stacks for each block that has been allocated and has not yet
    /// been freed.  If `ostream` is not specified, the value of `ostream`
    /// passed to the last call to `setOstream` will be used.  If
    /// `setOstream` was never called, `bsl::cerr` will be used.
    void reportBlocksInUse(bsl::ostream *ostream = 0) const;
};

                       // -----------------------------
                       // class StackTraceTestAllocator
                       // -----------------------------

inline
const StackTraceTestAllocator::FailureHandler&
StackTraceTestAllocator::failureHandler() const
{
    return d_failureHandler;
}

inline
bsl::size_t StackTraceTestAllocator::numBlocksInUse() const
{
    return d_numBlocksInUse;
}

inline
bsls::Types::Int64 StackTraceTestAllocator::numAllocations() const
{
    return d_numAllocations;
}

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