Skip to content

Commit

Permalink
Add allocation-limit to StackTraceTestAllocator. (#4963)
Browse files Browse the repository at this point in the history
  • Loading branch information
cppguru authored and GitHub Enterprise committed Sep 18, 2024
1 parent cc8d674 commit 7c958bd
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 26 deletions.
50 changes: 41 additions & 9 deletions groups/bal/balst/balst_stacktracetestallocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ BSLS_IDENT_RCSID(balst_stacktracetestallocator_cpp,"$Id$ $CSID$")
#include <bsla_maybeunused.h>

#include <bslma_allocator.h>
#include <bslma_testallocatorexception.h>
#include <bslma_mallocfreeallocator.h>

#include <bslmf_assert.h>
Expand All @@ -19,6 +20,7 @@ BSLS_IDENT_RCSID(balst_stacktracetestallocator_cpp,"$Id$ $CSID$")
#include <bsls_alignmentfromtype.h>
#include <bsls_alignmentutil.h>
#include <bsls_assert.h>
#include <bsls_buildtarget.h>
#include <bsls_platform.h>
#include <bsls_review.h>
#include <bsls_stackaddressutil.h>
Expand Down Expand Up @@ -228,7 +230,7 @@ int StackTraceTestAllocator::checkBlockHeader(
if (next) {
if (k_ALLOCATED_BLOCK_MAGIC != next->d_magic) {
if (k_DEALLOCATED_BLOCK_MAGIC == next->d_magic) {
*d_ostream << "Error: freed object on allocted block list"
*d_ostream << "Error: freed object on allocated block list"
<< " of allocator '" << d_name << "' at "
<< next << bsl::endl;
}
Expand Down Expand Up @@ -280,7 +282,7 @@ StackTraceTestAllocator::StackTraceTestAllocator(
BSLS_ASSERT(d_traceBufferLength >= d_maxRecordedFrames);

// This must be assigned in a statement in the body of the c'tor rather
// than in the initializer list to work around a microsoft bug with
// than in the initializer list to work around a Microsoft bug with
// function pointers.

d_failureHandler = &failAbort;
Expand All @@ -292,6 +294,7 @@ StackTraceTestAllocator::StackTraceTestAllocator(
: d_magic(k_STACK_TRACE_TEST_ALLOCATOR_MAGIC)
, d_numBlocksInUse(0)
, d_numAllocations(0)
, d_allocationLimit(-1)
, d_blocks(0)
, d_mutex()
, d_name("<unnamed>")
Expand All @@ -311,7 +314,7 @@ StackTraceTestAllocator::StackTraceTestAllocator(
BSLS_ASSERT(d_traceBufferLength >= d_maxRecordedFrames);

// This must be assigned in a statement in the body of the c'tor rather
// than in the initializer list to work around a microsoft bug with
// than in the initializer list to work around a Microsoft bug with
// function pointers.

d_failureHandler = &failAbort;
Expand All @@ -334,12 +337,32 @@ StackTraceTestAllocator::~StackTraceTestAllocator()
// MANIPULATORS
void *StackTraceTestAllocator::allocate(size_type size)
{
// All updates are protected by a mutex lock, so as to not interleave the
// action of multiple threads. Note that the lock is needed even for
// atomic variables, as concurrent writes to different statistics could put
// the variables in an inconsistent state.
bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);

// The 'd_numAllocations' is updated before attempting any allocations,
// even if later we throw because we have reached the allocation limit. In
// other words this statistic represents the number of *attempted*
// allocations, not the number of allocated blocks.
d_numAllocations.addRelaxed(1);

#ifdef BDE_BUILD_TARGET_EXC
if (0 <= allocationLimit()) {
// An exception-test allocation limit has been set. Decrement the
// limit and throw a special exception if it goes negative.
if (0 > d_allocationLimit.addRelaxed(-1)) {
throw bslma::TestAllocatorException(static_cast<int>(size));
}
}
#endif

if (0 == size) {
return 0; // RETURN
}

bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex);

// The underlying allocator might align the block differently depending on
// the size passed. The alignment must be large enough to accommodate the
// stack addresses (type 'void *') in the buffer, it must be large enough
Expand Down Expand Up @@ -387,8 +410,7 @@ void *StackTraceTestAllocator::allocate(size_type size)

BSLS_ASSERT(0 == ((UintPtr) ret & ((sizeof(void *) - 1) | lowBits)));

++d_numBlocksInUse;
++d_numAllocations;
d_numBlocksInUse.addRelaxed(1);

return ret;
}
Expand Down Expand Up @@ -430,7 +452,7 @@ void StackTraceTestAllocator::deallocate(void *address)
d_allocator_p->deallocate(reinterpret_cast<void **>(blockHdr) -
d_traceBufferLength);

--d_numBlocksInUse;
d_numBlocksInUse.addRelaxed(-1);
BSLS_ASSERT(d_numBlocksInUse >= 0);
}

Expand Down Expand Up @@ -462,7 +484,12 @@ void StackTraceTestAllocator::release()
d_blocks = 0;

BSLS_ASSERT(numBlocks == d_numBlocksInUse);
d_numBlocksInUse = 0;
d_numBlocksInUse.storeRelaxed(0);
}

void StackTraceTestAllocator::setAllocationLimit(bsls::Types::Int64 limit)
{
d_allocationLimit.storeRelaxed(limit);
}

void StackTraceTestAllocator::setDemanglingPreferredFlag(bool value)
Expand Down Expand Up @@ -497,6 +524,11 @@ void StackTraceTestAllocator::setOstream(bsl::ostream *ostream)
}

// ACCESSORS
bsls::Types::Int64 StackTraceTestAllocator::allocationLimit() const
{
return d_allocationLimit.loadRelaxed();
}

void StackTraceTestAllocator::reportBlocksInUse(bsl::ostream *ostream) const
{
typedef bsl::vector<const void *> StackTraceVec;
Expand Down
30 changes: 23 additions & 7 deletions groups/bal/balst/balst_stacktracetestallocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ BSLS_IDENT("$Id: $")
// `------------------------------'
// | ctor/dtor
// | numAllocations
// | allocationLimit
// | numBlocksInUse
// | reportBlocksInUse
// | setFailureHandler
Expand Down Expand Up @@ -522,15 +523,19 @@ class StackTraceTestAllocator : public bdlma::ManagedAllocator {
AllocatorMagic d_magic; // magic # to identify type
// of memory allocator

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

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

BlockHeader *d_blocks; // list of allocated,
// unfreed blocks
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
Expand Down Expand Up @@ -650,6 +655,12 @@ class StackTraceTestAllocator : public bdlma::ManagedAllocator {
/// 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
Expand Down Expand Up @@ -680,8 +691,13 @@ class StackTraceTestAllocator : public bdlma::ManagedAllocator {

// 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 observered.
/// failure is observed.
const FailureHandler& failureHandler() const;

/// Return the number of allocations from this object that have occurred
Expand Down
144 changes: 134 additions & 10 deletions groups/bal/balst/balst_stacktracetestallocator.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

#include <bdlb_random.h>
#include <bdlb_string.h>

#include <bdlma_bufferedsequentialallocator.h>

#include <bdls_filesystemutil.h>

#include <bslim_testutil.h>
Expand All @@ -16,9 +18,10 @@
#include <bslma_defaultallocatorguard.h>
#include <bslma_mallocfreeallocator.h>
#include <bslma_newdeleteallocator.h>
#include <bslma_testallocator.h>
#include <bslma_testallocatorexception.h> // for testing only
#include <bslma_testallocator.h> // for testing only
#include <bslma_testallocatorexception.h>
#include <bslma_testallocatormonitor.h>
#include <bslma_managedptr.h>

#include <bslmt_barrier.h>
#include <bslmt_threadutil.h>
Expand Down Expand Up @@ -76,7 +79,8 @@ using bsl::flush;
//
//-----------------------------------------------------------------------------
// [23] o usage example
// [22] o block header test -- use 'my_BlockHeader' to test
// [22] o CONCERN: Exception Test Loop Compatibility
// [21] o block header test -- use 'my_BlockHeader' to test
// o magic number
// o pointer to allocator
// o validity of next block
Expand Down Expand Up @@ -296,7 +300,7 @@ static int veryVerbose;
static int veryVeryVerbose;

static const bsl::size_t npos = bsl::string::npos;
static bool narcissicStack = false; // On Windows, the stack trace
static bool narcissictStack = false; // On Windows, the stack trace
// intermittently captures its own
// image.

Expand Down Expand Up @@ -1063,21 +1067,21 @@ int main(int argc, char *argv[])
ASSERT(voidFuncsSize <= sizeof voidFuncs / sizeof *voidFuncs);

if (e_WINDOWS) {
// Windows intermittently has a narcissic stack, that is,
// Windows intermittently has a narcissist stack, that is,
// 'k_IGNORE_FRAMES' should be 1 higher than it is.

balst::StackTrace st;
int rc = balst::StackTraceUtil::loadStackTraceFromStack(&st, 1, false);
ASSERT(0 == rc);

narcissicStack = bsl::string::npos != st[0].mangledSymbolName().find(
narcissictStack = bsl::string::npos != st[0].mangledSymbolName().find(
"loadStackTraceFromStack");

if (veryVerbose) P(narcissicStack);
if (veryVerbose) P(narcissictStack);
}

switch (test) { case 0:
case 22: {
case 23: {
// --------------------------------------------------------------------
// USAGE EXAMPLE
//
Expand Down Expand Up @@ -1244,6 +1248,126 @@ int main(int argc, char *argv[])
expectedDefaultAllocations = -1;
#endif
} break;
case 22: {
// --------------------------------------------------------------------
// CONCERN: Exception Test Loop Compatibility
// Verify that the object works together with the
// 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN' and
// 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END' macros.
//
// Concerns:
//: 1 If 'allocate' causes the allocator to throw a
//: 'bslma::TestAllocatorException' that exception is propagated to
//: the caller.
//:
//: 2 No resources are leaked if the allocator throws.
//:
//: 3 The number of allocations and number of allocated blocks counters
//: are updated properly on allocation's that throw.
//
// Plan:
//: 1 Create a 'StackTraceTestAllocator' to use as exception test
//: allocator.
//:
//: 2 Within a 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST' loop:
//: 1 Allocate a few bytes from the second allocator.
//: 2 Deallocate the allocated bytes.
//: 3 Allocate and deallocate 0 bytes.
//: 4 Perform one more allocation and deallocation.
//:
//: 3 Verify that no allocated blocks remain allocated from the
//: allocator once the exception loop succeeds. (C-2)
//:
//: 4 Verify that the exception loop ran three iterations,
//: indicating that all exceptions have occurred and been propagated
//: to the caller. (C-1)
//:
//: 5 Verify that the two counters are the expected values. (C-3)
//:
//: 6 Repeat with more allocations, and deallocations happening at the
//: end of the loop
//
// Testing:
// CONCERN: Exception Test Loop Compatibility
// --------------------------------------------------------------------

if (verbose) printf("\nCONCERN: Exception Test Loop Compatibility"
"\n==========================================\n");

#ifdef BDE_BUILD_TARGET_EXC
{
Obj obj;
int iterations = 0;
BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(obj) {
++iterations;
if (veryVeryVerbose) {
P_(iterations) P(obj.allocationLimit());
}

void* p = obj.allocate(13); // Might throw from allocator
ASSERT(p);
obj.deallocate(p);

p = obj.allocate(0); // Might throw from allocator
ASSERT(0 == p);
obj.deallocate(p);

p = obj.allocate(15); // Might throw from allocator
ASSERT(p);
obj.deallocate(p);

// If got here, then upstream allocator did not throw, and the
// exception loop will end.
} BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END;

ASSERTV(obj.numBlocksInUse(), 0 == obj.numBlocksInUse());
// 1 + 2 + 3 + 3 ==> 4 iterations, first 3 throws last succeeds
ASSERTV(iterations, 4 == iterations);
// 1 + 2 + 3 + 3 ==> obj.numAllocations() <== 9
ASSERTV(obj.numAllocations(), 9 == obj.numAllocations());
}

// Second test with smart pointer releasing at the end of the scope
{
Obj obj;
int iterations = 0;
BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(obj) {
++iterations;
if (veryVeryVerbose) {
P_(iterations) P(obj.allocationLimit());
}

bslma::ManagedPtr<char> p1; // Might throw
p1.load((char *)obj.allocate(13), &obj);
ASSERT(p1);

bslma::ManagedPtr<char> p2; // Might throw
p2.load((char*)obj.allocate(15), &obj);
ASSERT(p2);

bslma::ManagedPtr<char> p3; // Might throw
p3.load((char*)obj.allocate(0), &obj);
ASSERT(0 == p3.get());

bslma::ManagedPtr<char> p4; // Might throw
p4.load((char*)obj.allocate(146), &obj);
ASSERT(p4.get());

// If got here, then upstream allocator did not throw, and the
// exception loop will end.
} BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END;

ASSERTV(obj.numBlocksInUse(), 0 == obj.numBlocksInUse());
// 1 + 2 + 3 + 4 + 4 ==> 5 iterations, first 4 throws last succeeds
ASSERTV(iterations, 5 == iterations);
// 1 + 2 + 3 + 4 + 4 ==> obj.numAllocations() <== 14
ASSERTV(obj.numAllocations(), 14 == obj.numAllocations());
}
#else
if (verbose) printf("\nNo testing. Exceptions are not enabled.\n");
#endif // BDE_BUILD_TARGET_EXC

} break;
case 21: {
//---------------------------------------------------------------------
// WHITE-BOX EXAMINATION OF BLOCK HEADER
Expand Down Expand Up @@ -1419,8 +1543,8 @@ int main(int argc, char *argv[])
}

LOOP4_ASSERT(report, numRecurserInTrace, RECORDED_FRAMES,
narcissicStack,
numRecurserInTrace + 1 + narcissicStack == RECORDED_FRAMES);
narcissictStack,
numRecurserInTrace + 1 + narcissictStack == RECORDED_FRAMES);
ss.str("");
}
} break;
Expand Down

0 comments on commit 7c958bd

Please sign in to comment.